NetBSD/gnu/usr.bin/gas/config/tc-powerpc.c

1313 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
Copyright (C) 1994 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 "obstack.h"
#include "tc-powerpc.h"
#include "opcode/ppc.h"
/* This is the assembler for the PowerPC or POWER (RS/6000) chips. */
/* Tell the main code what the endianness is. */
int target_big_endian;
/* Whether or not, we've set target_big_endian. */
static int set_target_endian = 0;
static void ppc_set_cpu PARAMS((void));
static unsigned long ppc_insert_operand
PARAMS((unsigned long insn, const struct powerpc_operand *operand, long val));
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));
static void ppc_cons PARAMS((int));
static enum reloc_type ppc_suffix PARAMS((char **));
/* Generic assembler global variables which must be defined by all
targets. */
/* Characters which always start a comment. */
const char comment_chars[] = "#";
/* 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 },
{ "long", ppc_cons, 4 },
{ "word", ppc_cons, 2 },
{ "short", ppc_cons, 2 },
/* This pseudo-op is used even when not generating XCOFF output. */
{ "tc", ppc_tc, 0 },
{ NULL, NULL, 0 }
};
const relax_typeS md_relax_table[] = {
0,
};
int md_reloc_size = 12; /* Size of relocation record */
/* 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 int ppc_size = PPC_OPCODE_32;
/* Opcode hash table. */
static struct hash_control *ppc_hash;
/* Macro hash table. */
static struct hash_control *ppc_macro_hash;
symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
int
md_parse_option(argp, cntp, vecp)
char **argp;
int *cntp;
char ***vecp;
{
char *arg;
switch (**argp) {
#ifdef PIC
case 'k':
/* Predefine GOT symbol */
GOT_symbol = symbol_find_or_make("__GLOBAL_OFFSET_TABLE_");
#endif
case 'u':
/* -u means that any undefined symbols should be treated as
external, which is the default for gas anyhow. */
break;
case 'm':
arg = *argp + 1;
/* -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, "603") == 0
|| strcmp(arg, "604") == 0)
ppc_cpu = PPC_OPCODE_PPC;
/* -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;
}
/* -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;
/* -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 {
as_bad("invalid switch -m%s", arg);
return 0;
}
**argp = 0;
break;
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, -m603, -m604\n\
generate code for Motorola PowerPC 603/604\n\
-mppc64, -m620 generate code for Motorola PowerPC 620\n\
-mcom generate code Power/PowerPC common instructions\n\
-many generate code for any architecture (PWR/PWRX/PPC)\n");
fprintf(stream, "\
-mlittle, -mlittle-endian\n\
generate code for a little endian machine\n\
-mbig, -mbig-endian generate code for a big endian machine\n");
}
/* Set ppc_cpu if it is not already set. */
static void
ppc_set_cpu()
{
const char *default_cpu = TARGET_CPU;
if (ppc_cpu == 0) {
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", default_cpu);
}
}
/* This gets called to early */
void
md_begin()
{
}
static int begin_called = 0;
static void
really_begin()
{
register const struct powerpc_opcode *op;
const struct powerpc_opcode *op_end;
const struct powerpc_macro *macro;
const struct powerpc_macro *macro_end;
int dup_insn = 0;
begin_called = 1;
ppc_set_cpu();
/* 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)) {
const char *retval;
retval = hash_insert(ppc_hash, op->name, op);
if (*retval) {
/* 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 = 1;
}
}
}
/* 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, macro);
if (*retval) {
as_bad("Internal assembler error for macro %s", macro->name);
dup_insn = 1;
}
}
}
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 = 1;
}
}
/* Nothing to do at the moment */
void
md_end()
{
}
/* Parse an operand that is machine-specific */
/* ARGSUSED */
void
md_operand(exp)
expressionS *exp;
{
}
#define RELOC_PCRELATIVE(r) \
(((r) >= RELOC_REL24 && (r) <= RELOC_REL14_NTAKEN) \
|| (r) == RELOC_PLT24 \
|| (r) == RELOC_REL32 \
|| (r) == RELOC_PLTREL32)
static int reloc_target_size[] = {
-1, 4, 4, 2, 2, 2, 2, 4,
-1, -1, 4, 4, -1, -1, 2, 2,
2, 2, 4, -1, -1, -1, -1, -1,
-1, -1, 4, 4, 4, 2, 2, 2
};
/* Insert an operand value into an instruction. */
static unsigned long
ppc_insert_operand(insn, operand, val)
unsigned long insn;
const struct powerpc_operand *operand;
long val;
{
if (operand->bits != 32) {
long min, max;
long test;
#ifdef __notdef__
if ((operand->flags & PPC_OPERAND_SIGNED) != 0) {
if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0
&& ppc_size == PPC_OPCODE_32)
max = (1 << operand->bits) - 1;
else
max = (1 << (operand->bits - 1)) - 1;
min = - (1 << (operand->bits - 1));
} else {
max = (1 << operand->bits) - 1;
min = 0;
}
#else
/*
* For now, we allow both signed and unsigned operands
*/
min = - (1 << (operand->bits - 1));
max = (1 << operand->bits) - 1;
#endif
if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
test = - val;
else
test = val;
if (test < min || test > max) {
const char *err =
"operand out of range (%s not between %ld and %ld)";
char buf[100];
sprint_value(buf, test);
as_warn(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_warn(errmsg);
} else
insn |= (((long) val & ((1 << operand->bits) - 1))
<< operand->shift);
return insn;
}
/* Parse @got, etc. and return the desired relocation. */
static enum reloc_type
ppc_suffix(str_p)
char **str_p;
{
struct map_reloc {
char *string;
int length;
enum reloc_type reloc;
};
char ident[20];
char *str = *str_p;
char *str2;
int ch;
int len;
struct map_reloc *ptr;
#define MAP(str,reloc) { str, sizeof(str)-1, reloc }
static struct map_reloc mapping[] = {
MAP ("got", RELOC_GOT16),
MAP ("l", RELOC_16_LO),
MAP ("h", RELOC_16_HI),
MAP ("ha", RELOC_16_HA),
MAP ("brtaken", RELOC_REL14_TAKEN),
MAP ("brntaken", RELOC_REL14_NTAKEN),
MAP ("got@l", RELOC_GOT16_LO),
MAP ("got@h", RELOC_GOT16_HI),
MAP ("got@ha", RELOC_GOT16_HA),
MAP ("fixup", RELOC_32),
MAP ("pltrel24", RELOC_PLT24),
MAP ("copy", RELOC_COPY),
MAP ("globdat", RELOC_GLOB_DAT),
MAP ("local24pc", RELOC_LOCAL24PC),
MAP ("plt", RELOC_PLT32),
MAP ("pltrel", RELOC_PLTREL32),
MAP ("plt@l", RELOC_PLT16_LO),
MAP ("plt@h", RELOC_PLT16_HI),
MAP ("plt@ha", RELOC_PLT16_HA),
{ (char *)0, 0, RELOC_NONE }
};
if (*str++ != '@')
return RELOC_NONE;
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) {
*str_p = str;
return ptr->reloc;
}
return RELOC_NONE;
}
/* Like normal .long/.short/.word, except support @got, etc. */
/* clobbers input_line_pointer, checks */
/* end-of-line. */
static void
ppc_cons(nbytes)
register int nbytes; /* 1=.byte, 2=.word, 4=.long */
{
expressionS exp;
enum reloc_type reloc;
long get;
segT segment;
char *p;
if (!begin_called)
really_begin();
if (is_it_end_of_statement()) {
demand_empty_rest_of_line();
return;
}
do {
segment = expression(&exp);
if (!need_pass_2) {
know(segment != SEG_PASS1);
if (segment == SEG_DIFFERENCE && exp.X_add_symbol == NULL) {
as_bad("Subtracting symbol \"%s\"(segment\"%s\") is too hard. Absolute segment assumed.",
S_GET_NAME(exp.X_subtract_symbol),
segment_name(S_GET_SEGMENT(exp.X_subtract_symbol)));
segment = SEG_ABSOLUTE;
}
p = frag_more(nbytes);
switch (segment) {
case SEG_BIG:
as_bad("%s number invalid. Absolute 0 assumed.",
exp.X_add_number > 0 ? "Bignum" : "Floating-Point");
md_number_to_chars(p, (long)0, nbytes);
break;
case SEG_ABSENT:
as_warn("0 assumed for missing expression");
exp.X_add_number = 0;
know(exp.X_add_symbol == NULL);
/* FALLTHROUGH */
case SEG_ABSOLUTE:
get = exp.X_add_number;
md_number_to_chars(p, get, nbytes);
break;
default:
if (*input_line_pointer == '@'
&& (reloc = ppc_suffix(&input_line_pointer)) != RELOC_NONE) {
int size = reloc_target_size[reloc];
int offset = nbytes - size;
fix_new(frag_now,
p - frag_now->fr_literal + offset,
size,
exp.X_add_symbol, exp.X_subtract_symbol,
exp.X_add_number, 0,
reloc
#ifdef PIC
,exp.X_got_symbol
#endif
);
} else
fix_new(frag_now, p - frag_now->fr_literal, nbytes,
exp.X_add_symbol, exp.X_subtract_symbol,
exp.X_add_number, 0,
RELOC_32,
#ifdef PIC
exp.X_got_symbol
#endif
);
break;
}
}
} while (*input_line_pointer++ == ',');
input_line_pointer--; /* Put terminator back into stream. */
demand_empty_rest_of_line();
}
/* 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;
enum reloc_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;
enum reloc_type reloc;
if (!begin_called)
really_begin();
/* 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_warn(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_warn(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;
expression(&ex);
str = input_line_pointer;
input_line_pointer = hold;
if (ex.X_add_symbol == NULL
&& ex.X_subtract_symbol == NULL) {
/* Allow @HA, @L, @H on constants. */
char *orig_str = str;
if ((reloc = ppc_suffix(&str)) != RELOC_NONE)
switch (reloc) {
default:
str = orig_str;
break;
case RELOC_16_LO:
ex.X_add_number &= 0xffff;
break;
case RELOC_16_HI:
ex.X_add_number = (ex.X_add_number >> 16) & 0xffff;
break;
case RELOC_16_HA:
ex.X_add_number = ((ex.X_add_number >> 16) & 0xffff)
+ ((ex.X_add_number >> 15) & 1);
break;
}
insn = ppc_insert_operand(insn, operand, ex.X_add_number);
} else if ((reloc = ppc_suffix(&str)) != RELOC_NONE) {
/* 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 RELOC_REL24:
reloc = RELOC_24;
break;
case RELOC_REL14:
reloc = RELOC_14;
break;
case RELOC_REL14_TAKEN:
reloc = RELOC_14_TAKEN;
break;
case RELOC_REL14_NTAKEN:
reloc = RELOC_14_NTAKEN;
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;
} 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 = RELOC_NONE;
++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);
/* Create any fixups. At this point we do not use a
enum reloc_type, but instead just use the
RELOC_NONE 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 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 != RELOC_NONE) {
int size = reloc_target_size[fixups[i].reloc];
int pcrel = RELOC_PCRELATIVE(fixups[i].reloc);
int offset;
offset = target_big_endian ? (4 - size) : 0;
if (size < 1 || size > 4)
abort();
fix_new(frag_now, f - frag_now->fr_literal + offset, size,
fixups[i].exp.X_add_symbol,
fixups[i].exp.X_subtract_symbol,
fixups[i].exp.X_add_number,
pcrel, fixups[i].reloc
#ifdef PIC
,fixups[i].exp.X_got_symbol
#endif
);
} else
fix_new(frag_now, f - frag_now->fr_literal, 4,
fixups[i].exp.X_add_symbol,
fixups[i].exp.X_subtract_symbol,
fixups[i].exp.X_add_number,
(operand->flags & PPC_OPERAND_RELATIVE) != 0,
(enum reloc_type)(fixups[i].opindex + (int)RELOC_MAX)
#ifdef PIC
,fixups[i].exp.X_got_symbol
#endif
);
}
}
/* 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);
}
/* 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;
{
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();
}
/* The .tc pseudo-op. This is used when generating either XCOFF or
ELF. This takes two or more arguments.
When generating XCOFF output, the first argument is the name to
give to this location in the toc; this will be a symbol with class
TC. The rest of the arguments are 4 byte values to actually put at
this location in the TOC; often there is just one more argument, a
relocateable symbol reference.
When not generating XCOFF output, the arguments are the same, but
the first argument is simply ignored. */
static void
ppc_tc(ignore)
int ignore;
{
/* Skip the TOC symbol name. */
while (is_part_of_name(*input_line_pointer)
|| *input_line_pointer == '['
|| *input_line_pointer == ']'
|| *input_line_pointer == '{'
|| *input_line_pointer == '}')
++input_line_pointer;
/* Align to a four byte boundary. */
frag_align(2, 0);
record_alignment(now_seg, 2);
if (*input_line_pointer != ',')
demand_empty_rest_of_line();
else {
++input_line_pointer;
cons(4);
}
}
/* Turn a string in input_line_pointer into a floating point constant
of type type, and store the appropriate bytes in *litp. The number
of LITTLENUMS emitted is stored in *sizep . An error message is
returned. */
char *
md_atof(type, litp, sizep)
int type;
char *litp;
int *sizep;
{
int prec;
LITTLENUM_TYPE words[4];
char *t;
int i;
if (!begin_called)
really_begin();
switch (type) {
case 'f':
prec = 2;
break;
case 'd':
prec = 4;
break;
default:
*sizep = 0;
return "bad call to md_atof";
}
t = atof_ieee(input_line_pointer, type, words);
if (t)
input_line_pointer = t;
*sizep = prec * 2;
if (target_big_endian) {
for (i = 0; i < prec; i++) {
md_number_to_chars(litp, (valueT) words[i], 2);
litp += 2;
}
} else {
for (i = prec - 1; i >= 0; i--) {
md_number_to_chars(litp, (valueT) words[i], 2);
litp += 2;
}
}
return "";
}
/* Write a value out to the object file, using the appropriate
endianness. */
void
md_number_to_chars(buf, val, n)
char *buf;
long val;
int n;
{
if (!begin_called)
really_begin();
if (target_big_endian)
buf += n;
while (--n >= 0) {
if (target_big_endian)
*--buf = val;
else
*buf++ = val;
val >>= 8;
}
}
/* Align a section (I don't know why this is machine dependent). */
long
md_section_align(seg, addr)
segT seg;
long addr;
{
/* Align all sections to 128-bit boundaries */
return (addr + 15) & ~15;
}
/* We don't have any form of relaxing. */
int
md_estimate_size_before_relax(fragp, seg)
fragS *fragp;
segT seg;
{
abort();
return 0;
}
/* Convert a machine dependent frag. We never generate these. */
void
md_convert_frag(headers, fragp)
object_headers *headers;
fragS *fragp;
{
abort();
}
/* We have no need to default values of symbols. */
/*ARGSUSED*/
symbolS *
md_undefined_symbol(name)
char *name;
{
return 0;
}
/* Functions concerning relocs. */
/* The location from which a PC relative jump should be calculated,
given a PC relative reloc. */
long
md_pcrel_from(fixp)
fixS *fixp;
{
return fixp->fx_frag->fr_address + fixp->fx_where;
}
/* See whether a symbol is in the TOC section. FIXME (ws) */
static int
ppc_is_toc_sym(sym)
symbolS *sym;
{
return strcmp(segment_name(S_GET_SEGMENT(sym)), ".got") == 0;
}
static long
md_getl(p)
unsigned char *p;
{
if (target_big_endian)
return (p[0] << 24)
| (p[1] << 16)
| (p[2] << 8)
| p[3];
else
return (p[3] << 24)
| (p[2] << 16)
| (p[1] << 8)
| p[0];
}
static void
md_putl(l, p)
unsigned long l;
unsigned char *p;
{
if (target_big_endian) {
*p++ = l >> 24;
*p++ = l >> 16;
*p++ = l >> 8;
*p++ = l;
} else {
*p++ = l;
*p++ = l >> 8;
*p++ = l >> 16;
*p++ = l >> 24;
}
}
/* Apply a fixup to the object code. This is called for all the
fixups we generated by the call to fix_new_exp, above. In the call
above we used a reloc code which was the largest legal reloc code
plus the operand index. Here we undo that to recover the operand
index. At this point all symbol values should be fully resolved,
and we attempt to completely resolve the reloc. If we can not do
that, we determine the correct reloc code and put it back in the
fixup. */
void
md_apply_fix(fixp, val)
fixS *fixp;
long val;
{
/* Remember value for reloc entry */
fixp->fx_addnumber = val;
if (!fixp->fx_pcrel && fixp->fx_addsy) {
val = fixp->fx_offset;
if (fixp->fx_subsy != (symbolS *)NULL) {
/* We can't actually support subtracting a symbol. */
as_bad("expression too complex");
}
}
if ((int)fixp->fx_r_type >= (int)RELOC_MAX) {
int opindex;
const struct powerpc_operand *operand;
char *where;
unsigned long insn;
opindex = (int)fixp->fx_r_type - (int)RELOC_MAX;
operand = &powerpc_operands[opindex];
/* Fetch the instruction, insert the fully resolved operand
value, and stuff the instruction back again. */
where = fixp->fx_frag->fr_literal + fixp->fx_where;
insn = md_getl((unsigned char *)where);
insn = ppc_insert_operand(insn, operand, val);
md_putl(insn, (unsigned char *)where);
/* Determine a reloc value based on the operand information.
We are only prepared to turn a few of the operands into
relocs.
FIXME: We need to handle the DS field at the very least.
FIXME: Selecting the reloc type is a bit haphazard; perhaps
there should be a new field in the operand table. */
if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
&& operand->bits == 26
&& operand->shift == 0)
fixp->fx_r_type = RELOC_REL24;
else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
&& operand->bits == 16
&& operand->shift == 0)
fixp->fx_r_type = RELOC_REL14;
else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
&& operand->bits == 26
&& operand->shift == 0)
fixp->fx_r_type = RELOC_24;
else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
&& operand->bits == 16
&& operand->shift == 0)
fixp->fx_r_type = RELOC_14;
else if ((operand->flags & PPC_OPERAND_PARENS) != 0
&& operand->bits == 16
&& operand->shift == 0
&& operand->insert == NULL
&& fixp->fx_addsy != NULL
&& ppc_is_toc_sym(fixp->fx_addsy)) {
fixp->fx_size = 2;
if (target_big_endian)
fixp->fx_where += 2;
fixp->fx_r_type = RELOC_GOT16;
} else if (fixp->fx_addsy == NULL
&& fixp->fx_subsy == NULL)
fixp->fx_r_type = RELOC_NONE;
else {
as_bad("unresolved expression that must be resolved");
if (fixp->fx_addsy)
ps(fixp->fx_addsy);
if (fixp->fx_subsy) {
fprintf(stderr, "-");
ps(fixp->fx_subsy);
}
fprintf(stderr,"\n");
return;
}
} else {
switch (fixp->fx_r_type) {
case RELOC_32:
if (fixp->fx_pcrel) {
fixp->fx_r_type = RELOC_REL32;
val += fixp->fx_frag->fr_address + fixp->fx_where;
} /* fall through */
case RELOC_REL32:
md_number_to_chars(fixp->fx_frag->fr_literal + fixp->fx_where,
val, 4);
break;
case RELOC_16_HA:
val += (val << 1) & 0x8000;
/* Fall Through */
case RELOC_16_HI:
val >>= 16;
/* Fall Through */
case RELOC_16_LO:
case RELOC_GOT16:
case RELOC_16:
if (fixp->fx_pcrel)
abort();
md_number_to_chars(fixp->fx_frag->fr_literal + fixp->fx_where,
val, 2);
break;
default:
fprintf(stderr,
"Gas failure, reloc value %d\n", fixp->fx_r_type);
fflush(stderr);
abort();
}
}
}
void
tc_aout_fix_to_chars(where, fixp, segment_address)
char *where;
fixS *fixp;
relax_addressT segment_address;
{
long r_index;
long r_extern;
long r_addend = 0;
long r_address;
#ifdef PIC
int kflag = 0;
#endif
know(fixp->fx_addsy);
if (!S_IS_DEFINED(fixp->fx_addsy)) {
r_extern = 1;
r_index = fixp->fx_addsy->sy_number;
} else {
r_extern = 0;
r_index = S_GET_TYPE(fixp->fx_addsy);
#ifdef PIC
if (flagseen['k'])
as_bad("tc_aout_fix_to_chars for -k to be done");
#endif
}
md_number_to_chars(where,
r_address = fixp->fx_frag->fr_address
+ fixp->fx_where
- segment_address,
4);
/* This is probably wrong for little-endian, but... */
where[4] = r_index >> 16;
where[5] = r_index >> 8;
where[6] = r_index;
where[7] = ((r_extern << 7) & 0x80) | (fixp->fx_r_type & 0x1f);
if (fixp->fx_addsy->sy_frag)
r_addend = fixp->fx_addsy->sy_frag->fr_address;
if (fixp->fx_pcrel) {
#ifdef PIC
if (fixp->fx_gotsy) {
r_addend = r_address;
r_addend += fixp->fx_addnumber;
} else
#endif
r_addend -= r_address;
} else {
#ifdef PIC
if (kflag)
r_addend = 0;
else
#endif
r_addend = fixp->fx_addnumber;
}
md_number_to_chars(&where[8], r_addend, 4);
}