#include <stdio.h>
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "struc-symbol.h"
#include "opcode/i370.h"
#ifdef OBJ_ELF
#include "elf/i370.h"
#endif
extern int target_big_endian;
#ifdef OBJ_ELF
static const char i370_eabi_comment_chars[] = "#";
const char *i370_comment_chars = i370_eabi_comment_chars;
#else
const char comment_chars[] = "#";
#endif
const char line_comment_chars[] = "#*";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";
void
md_show_usage (stream)
FILE *stream;
{
fprintf (stream, "\
S/370 options: (these have not yet been tested and may not work) \n\
-u ignored\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\
-V print assembler version number\n");
#endif
}
static void i370_byte PARAMS ((int));
static void i370_tc PARAMS ((int));
static void i370_ebcdic PARAMS ((int));
static void i370_dc PARAMS ((int));
static void i370_ds PARAMS ((int));
static void i370_rmode PARAMS ((int));
static void i370_csect PARAMS ((int));
static void i370_dsect PARAMS ((int));
static void i370_ltorg PARAMS ((int));
static void i370_using PARAMS ((int));
static void i370_drop PARAMS ((int));
static void i370_make_relative PARAMS ((expressionS *exp, expressionS *baseaddr));
#ifdef OBJ_ELF
static bfd_reloc_code_real_type i370_elf_suffix PARAMS ((char **, expressionS *));
static void i370_elf_cons PARAMS ((int));
static void i370_elf_rdata PARAMS ((int));
static void i370_elf_lcomm PARAMS ((int));
static void i370_elf_validate_fix PARAMS ((fixS *, segT));
#endif
const pseudo_typeS md_pseudo_table[] =
{
{ "byte", i370_byte, 0 },
{ "dc", i370_dc, 0 },
{ "ds", i370_ds, 0 },
{ "rmode", i370_rmode, 0 },
{ "csect", i370_csect, 0 },
{ "dsect", i370_dsect, 0 },
{ "ebcdic", i370_ebcdic, 0 },
#ifdef OBJ_ELF
{ "long", i370_elf_cons, 4 },
{ "word", i370_elf_cons, 4 },
{ "short", i370_elf_cons, 2 },
{ "rdata", i370_elf_rdata, 0 },
{ "rodata", i370_elf_rdata, 0 },
{ "lcomm", i370_elf_lcomm, 0 },
#endif
{ "tc", i370_tc, 0 },
{ "ltorg", i370_ltorg, 0 },
{ "using", i370_using, 0 },
{ "drop", i370_drop, 0 },
{ NULL, NULL, 0 }
};
#define TARGET_REG_NAMES_P TRUE
static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
static bfd_boolean register_name PARAMS ((expressionS *));
static void i370_set_cpu PARAMS ((void));
static i370_insn_t i370_insert_operand
PARAMS ((i370_insn_t insn, const struct i370_operand *operand, offsetT val));
static void i370_macro PARAMS ((char *str, const struct i370_macro *macro));
struct pd_reg
{
char *name;
int value;
};
static const struct pd_reg pre_defined_registers[] =
{
{ "arg", 11 },
{ "base", 3 },
{ "f.0", 0 },
{ "f.2", 2 },
{ "f.4", 4 },
{ "f.6", 6 },
{ "f0", 0 },
{ "f2", 2 },
{ "f4", 4 },
{ "f6", 6 },
{ "dsa",13 },
{ "lr", 14 },
{ "pgt", 4 },
{ "r.0", 0 },
{ "r.1", 1 },
{ "r.10", 10 },
{ "r.11", 11 },
{ "r.12", 12 },
{ "r.13", 13 },
{ "r.14", 14 },
{ "r.15", 15 },
{ "r.2", 2 },
{ "r.3", 3 },
{ "r.4", 4 },
{ "r.5", 5 },
{ "r.6", 6 },
{ "r.7", 7 },
{ "r.8", 8 },
{ "r.9", 9 },
{ "r.arg", 11 },
{ "r.base", 3 },
{ "r.dsa", 13 },
{ "r.pgt", 4 },
{ "r.sp", 13 },
{ "r.tca", 12 },
{ "r.toc", 12 },
{ "r0", 0 },
{ "r1", 1 },
{ "r10", 10 },
{ "r11", 11 },
{ "r12", 12 },
{ "r13", 13 },
{ "r14", 14 },
{ "r15", 15 },
{ "r2", 2 },
{ "r3", 3 },
{ "r4", 4 },
{ "r5", 5 },
{ "r6", 6 },
{ "r7", 7 },
{ "r8", 8 },
{ "r9", 9 },
{ "rbase", 3 },
{ "rtca", 12 },
{ "rtoc", 12 },
{ "sp", 13 },
};
#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
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;
}
static bfd_boolean
register_name (expressionP)
expressionS *expressionP;
{
int reg_number;
char *name;
char *start;
char c;
start = name = input_line_pointer;
if (name[0] == '%' && ISALPHA (name[1]))
name = ++input_line_pointer;
else if (!reg_names_p)
return FALSE;
while (' ' == *name)
name = ++input_line_pointer;
if (!ISALPHA (name[0]))
{
reg_number = get_single_number ();
}
else
{
c = get_symbol_end ();
reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
*input_line_pointer = c;
}
if ((0 <= reg_number) && (16 >= reg_number))
{
expressionP->X_op = O_register;
expressionP->X_add_number = reg_number;
expressionP->X_add_symbol = NULL;
expressionP->X_op_symbol = NULL;
return TRUE;
}
input_line_pointer = start;
return FALSE;
}
static int i370_cpu = 0;
static int i370_using_text_regno = -1;
static int i370_using_other_regno = -1;
static expressionS i370_using_text_baseaddr;
static expressionS i370_using_other_baseaddr;
static segT i370_other_section = undefined_section;
static struct hash_control *i370_hash;
static struct hash_control *i370_macro_hash;
#ifdef OBJ_ELF
static enum { SHLIB_NONE, SHLIB_PIC, SHILB_MRELOCATABLE } shlib = SHLIB_NONE;
#endif
static flagword i370_flags = 0;
#ifndef WORKING_DOT_WORD
int md_short_jump_size = 4;
int md_long_jump_size = 4;
#endif
#ifdef OBJ_ELF
const char *md_shortopts = "l:um: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':
break;
#ifdef OBJ_ELF
case 'K':
if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0)
{
shlib = SHLIB_PIC;
i370_flags |= EF_I370_RELOCATABLE_LIB;
}
else
return 0;
break;
#endif
case 'm':
if (strcmp (arg, "360") == 0 || strcmp (arg, "i360") == 0)
i370_cpu = I370_OPCODE_360;
else if (strcmp (arg, "xa") == 0)
i370_cpu = I370_OPCODE_370_XA;
else if (strcmp (arg, "any") == 0)
i370_cpu = I370_OPCODE_370;
else if (strcmp (arg, "regnames") == 0)
reg_names_p = TRUE;
else if (strcmp (arg, "no-regnames") == 0)
reg_names_p = FALSE;
#ifdef OBJ_ELF
else if (strcmp (arg, "relocatable") == 0)
{
shlib = SHILB_MRELOCATABLE;
i370_flags |= EF_I370_RELOCATABLE;
}
else if (strcmp (arg, "relocatable-lib") == 0)
{
shlib = SHILB_MRELOCATABLE;
i370_flags |= EF_I370_RELOCATABLE_LIB;
}
#endif
else
{
as_bad ("invalid switch -m%s", arg);
return 0;
}
break;
#ifdef OBJ_ELF
case 'V':
print_version_id ();
break;
case 'Q':
break;
#endif
default:
return 0;
}
return 1;
}
static void
i370_set_cpu ()
{
const char *default_os = TARGET_OS;
const char *default_cpu = TARGET_CPU;
i370_cpu = I370_OPCODE_ESA390_SUPERSET;
if (i370_cpu == 0)
{
if (strcmp (default_cpu, "i360") == 0)
i370_cpu = I370_OPCODE_360;
else if (strcmp (default_cpu, "i370") == 0)
i370_cpu = I370_OPCODE_370;
else if (strcmp (default_cpu, "XA") == 0)
i370_cpu = I370_OPCODE_370_XA;
else
as_fatal ("Unknown default cpu = %s, os = %s", default_cpu, default_os);
}
}
enum bfd_architecture
i370_arch ()
{
return bfd_arch_i370;
}
void
md_begin ()
{
register const struct i370_opcode *op;
const struct i370_opcode *op_end;
const struct i370_macro *macro;
const struct i370_macro *macro_end;
bfd_boolean dup_insn = FALSE;
i370_set_cpu ();
#ifdef OBJ_ELF
if (i370_flags)
bfd_set_private_flags (stdoutput, i370_flags);
#endif
i370_hash = hash_new ();
op_end = i370_opcodes + i370_num_opcodes;
for (op = i370_opcodes; op < op_end; op++)
{
know ((op->opcode.i[0] & op->mask.i[0]) == op->opcode.i[0]
&& (op->opcode.i[1] & op->mask.i[1]) == op->opcode.i[1]);
if ((op->flags & i370_cpu) != 0)
{
const char *retval;
retval = hash_insert (i370_hash, op->name, (PTR) op);
if (retval != (const char *) NULL)
{
as_bad ("Internal assembler error for instruction %s", op->name);
dup_insn = TRUE;
}
}
}
i370_macro_hash = hash_new ();
macro_end = i370_macros + i370_num_macros;
for (macro = i370_macros; macro < macro_end; macro++)
{
if ((macro->flags & i370_cpu) != 0)
{
const char *retval;
retval = hash_insert (i370_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 ();
}
static i370_insn_t
i370_insert_operand (insn, operand, val)
i370_insn_t insn;
const struct i370_operand *operand;
offsetT val;
{
if (operand->insert)
{
const char *errmsg;
errmsg = NULL;
insn = (*operand->insert) (insn, (long) val, &errmsg);
if (errmsg)
as_bad ("%s", errmsg);
}
else
{
insn.i[0] |= (((long) val & ((1 << operand->bits) - 1))
<< operand->shift);
}
return insn;
}
#ifdef OBJ_ELF
static bfd_reloc_code_real_type
i370_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 ("fixup", BFD_RELOC_CTOR),
{ (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++ = 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");
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;
}
static void
i370_elf_cons (nbytes)
register int nbytes;
{
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 = i370_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--;
demand_empty_rest_of_line ();
}
static unsigned char ascebc[256] =
{
0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F,
0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF
};
unsigned char ebcasc[256] =
{
0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7F,
0x00, 0x00, 0x00, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x0A, 0x08, 0x00,
0x18, 0x19, 0x00, 0x00, 0x1C, 0x1D, 0x1E, 0x1F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x1B,
0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1A,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
0x71, 0x72, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00,
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF
};
static void
i370_ebcdic (unused)
int unused ATTRIBUTE_UNUSED;
{
char *p, *end;
char delim = 0;
size_t nbytes;
nbytes = strlen (input_line_pointer);
end = input_line_pointer + nbytes;
while ('\r' == *end) end --;
while ('\n' == *end) end --;
delim = *input_line_pointer;
if (('\'' == delim) || ('\"' == delim)) {
input_line_pointer ++;
end = rindex (input_line_pointer, delim);
}
if (end > input_line_pointer)
{
nbytes = end - input_line_pointer +1;
p = frag_more (nbytes);
while (end > input_line_pointer)
{
*p = ascebc [(unsigned char) (*input_line_pointer)];
++p; ++input_line_pointer;
}
*p = '\0';
}
if (delim == *input_line_pointer) ++input_line_pointer;
}
static void
i370_rmode (unused)
int unused ATTRIBUTE_UNUSED;
{
as_tsktsk ("rmode ignored");
}
static void
i370_dsect (sect)
int sect;
{
char *save_line = input_line_pointer;
static char section[] = ".data\n";
input_line_pointer = section;
obj_elf_section (sect);
input_line_pointer = save_line;
}
static void
i370_csect (unused)
int unused ATTRIBUTE_UNUSED;
{
as_tsktsk ("csect not supported");
}
static void
i370_dc (unused)
int unused ATTRIBUTE_UNUSED;
{
char * p, tmp[50];
int nbytes=0;
expressionS exp;
char type=0;
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
type = *input_line_pointer++;
switch (type)
{
case 'H':
nbytes = 2;
break;
case 'E':
case 'F':
nbytes = 4;
break;
case 'D':
nbytes = 8;
break;
default:
as_bad ("unsupported DC type");
return;
}
if ('\'' == *input_line_pointer)
{
char * close;
++input_line_pointer;
close = strchr (input_line_pointer, '\'');
if (close)
*close= ' ';
else
as_bad ("missing end-quote");
}
if ('\"' == *input_line_pointer)
{
char * close;
++input_line_pointer;
close = strchr (input_line_pointer, '\"');
if (close)
*close= ' ';
else
as_bad ("missing end-quote");
}
switch (type)
{
case 'H':
case 'F':
expression (&exp);
emit_expr (&exp, nbytes);
break;
case 'E':
case 'D':
md_atof (type, tmp, &nbytes);
p = frag_more (nbytes);
memcpy (p, tmp, nbytes);
break;
default:
as_bad ("unsupported DC type");
return;
}
demand_empty_rest_of_line ();
}
static void
i370_ds (unused)
int unused ATTRIBUTE_UNUSED;
{
if ('0' == *input_line_pointer)
{
int alignment = 0;
input_line_pointer ++;
switch (*input_line_pointer++)
{
case 'H':
alignment = 1;
break;
case 'F':
alignment = 2;
break;
case 'D':
alignment = 3;
break;
default:
as_bad ("unsupported alignment");
return;
}
frag_align (alignment, 0, 0);
record_alignment (now_seg, alignment);
}
else
{
as_bad ("this DS form not yet supported");
}
}
static void
i370_elf_rdata (sect)
int sect;
{
char *save_line = input_line_pointer;
static char section[] = ".rodata\n";
input_line_pointer = section;
obj_elf_section (sect);
input_line_pointer = save_line;
}
static void
i370_elf_lcomm (unused)
int unused 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 ();
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++;
if ((size = get_absolute_expression ()) < 0)
{
as_warn (".COMMon length (%ld.) <0! Ignored.", (long) size);
ignore_rest_of_line ();
return;
}
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;
}
old_sec = now_seg;
old_subsec = now_subseg;
if (align)
{
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 ();
}
static void
i370_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 SHILB_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), ".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
#define LITERAL_POOL_SUPPORT
#ifdef LITERAL_POOL_SUPPORT
#define MAX_LITERAL_POOL_SIZE 1024
typedef struct literalS
{
struct expressionS exp;
char * sym_name;
char size;
short offset;
} literalT;
literalT literals[MAX_LITERAL_POOL_SIZE];
int next_literal_pool_place = 0;
static symbolS *longlong_poolP = NULL;
static symbolS *word_poolP = NULL;
static symbolS *short_poolP = NULL;
static symbolS *byte_poolP = NULL;
static int lit_pool_num = 1;
static symbolS *
symbol_make_empty (void)
{
return symbol_create (FAKE_LABEL_NAME, undefined_section,
(valueT) 0, &zero_address_frag);
}
static void
add_to_lit_pool (expressionS *exx, char *name, int sz)
{
int lit_count = 0;
int offset_in_pool = 0;
if (8 == sz && NULL == longlong_poolP)
longlong_poolP = symbol_make_empty ();
else if (4 == sz && NULL == word_poolP)
word_poolP = symbol_make_empty ();
else if (2 == sz && NULL == short_poolP)
short_poolP = symbol_make_empty ();
else if (1 == sz && NULL == byte_poolP)
byte_poolP = symbol_make_empty ();
while (lit_count < next_literal_pool_place)
{
if (exx->X_op == O_constant
&& literals[lit_count].exp.X_op == exx->X_op
&& literals[lit_count].exp.X_add_number == exx->X_add_number
&& literals[lit_count].exp.X_unsigned == exx->X_unsigned
&& literals[lit_count].size == sz)
break;
else if (literals[lit_count].sym_name
&& name
&& !strcmp (name, literals[lit_count].sym_name))
break;
if (sz == literals[lit_count].size)
offset_in_pool += sz;
lit_count ++;
}
if (lit_count == next_literal_pool_place)
{
if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
{
as_bad ("Literal Pool Overflow");
}
literals[next_literal_pool_place].exp = *exx;
literals[next_literal_pool_place].size = sz;
literals[next_literal_pool_place].offset = offset_in_pool;
if (name)
{
literals[next_literal_pool_place].sym_name = strdup (name);
}
else
{
literals[next_literal_pool_place].sym_name = NULL;
}
next_literal_pool_place++;
}
if (8 == sz)
exx->X_add_symbol = longlong_poolP;
else if (4 == sz)
exx->X_add_symbol = word_poolP;
else if (2 == sz)
exx->X_add_symbol = short_poolP;
else if (1 == sz)
exx->X_add_symbol = byte_poolP;
exx->X_add_number = offset_in_pool;
exx->X_op_symbol = NULL;
if (0 < i370_using_other_regno)
{
i370_make_relative (exx, &i370_using_other_baseaddr);
}
else
{
i370_make_relative (exx, &i370_using_text_baseaddr);
}
}
static void symbol_locate
PARAMS ((symbolS *, const char *, segT, valueT, fragS *));
static void
symbol_locate (symbolP, name, segment, valu, frag)
symbolS *symbolP;
const char *name;
segT segment;
valueT valu;
fragS *frag;
{
size_t name_length;
char *preserved_copy_of_name;
name_length = strlen (name) + 1;
obstack_grow (¬es, name, name_length);
preserved_copy_of_name = obstack_finish (¬es);
S_SET_NAME (symbolP, preserved_copy_of_name);
S_SET_SEGMENT (symbolP, segment);
S_SET_VALUE (symbolP, valu);
symbol_clear_list_pointers (symbolP);
symbol_set_frag (symbolP, frag);
{
extern int symbol_table_frozen;
if (symbol_table_frozen)
abort ();
}
symbol_append (symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP);
obj_symbol_new_hook (symbolP);
#ifdef tc_symbol_new_hook
tc_symbol_new_hook (symbolP);
#endif
#define DEBUG_SYMS
#ifdef DEBUG_SYMS
verify_symbol_chain(symbol_rootP, symbol_lastP);
#endif
}
static bfd_boolean
i370_addr_offset (expressionS *exx)
{
char *dot, *lab;
int islabel = 0;
int all_digits = 0;
lab = input_line_pointer;
while (*lab && (',' != *lab) && ('(' != *lab))
{
if (ISDIGIT (*lab))
{
all_digits = 1;
}
else if (ISALPHA (*lab))
{
if (!all_digits)
{
islabel = 1;
break;
}
else if (('f' == *lab) || ('b' == *lab))
{
islabel = 1;
break;
}
if (all_digits)
break;
}
else if ('.' != *lab)
break;
++lab;
}
dot = strchr (input_line_pointer, '*');
if (!dot && !islabel)
return FALSE;
if (dot)
*dot = '.';
expression (exx);
if (0 == strncmp (now_seg->name, ".text", 5) || 0 > i370_using_other_regno)
{
i370_make_relative (exx, &i370_using_text_baseaddr);
}
else
{
i370_make_relative (exx, &i370_using_other_baseaddr);
}
if (dot)
*dot = '*';
return TRUE;
}
static bfd_boolean
i370_addr_cons (expressionS *exp)
{
char *name;
char *sym_name, delim;
int name_len;
int hex_len=0;
int cons_len=0;
name = input_line_pointer;
sym_name = input_line_pointer;
if (name[0] == '=' && ISALPHA (name[1]))
{
name = ++input_line_pointer;
}
else
{
return FALSE;
}
switch (name[0])
{
case 'A':
case 'V':
++input_line_pointer;
expression (exp);
name_len = strcspn (sym_name, ", ");
delim = *(sym_name + name_len);
*(sym_name + name_len) = 0x0;
add_to_lit_pool (exp, sym_name, 4);
*(sym_name + name_len) = delim;
break;
case 'H':
case 'F':
case 'X':
case 'E':
case 'D':
if ('H' == name[0]) cons_len = 2;
else if ('F' == name[0]) cons_len = 4;
else if ('X' == name[0]) cons_len = -1;
else if ('E' == name[0]) cons_len = 4;
else if ('D' == name[0]) cons_len = 8;
if ('L' == name[1])
{
cons_len = name[2] - '0';
input_line_pointer += 2;
}
++input_line_pointer;
if ('\'' == *input_line_pointer)
{
char * close;
++input_line_pointer;
close = strchr (input_line_pointer, '\'');
if (close)
*close= ' ';
else
as_bad ("missing end-quote");
}
if ('\"' == *input_line_pointer)
{
char * close;
++input_line_pointer;
close = strchr (input_line_pointer, '\"');
if (close)
*close= ' ';
else
as_bad ("missing end-quote");
}
if (('X' == name[0]) || ('E' == name[0]) || ('D' == name[0]))
{
char tmp[50];
char *save;
if (('X' == name[0]) && (0 > cons_len))
{
save = input_line_pointer;
while (*save)
{
if (ISXDIGIT (*save))
hex_len++;
save++;
}
cons_len = (hex_len+1) /2;
}
tmp[0] = '0';
tmp[1] = name[0];
tmp[2] = 0;
strcat (tmp, input_line_pointer);
save = input_line_pointer;
input_line_pointer = tmp;
expression (exp);
input_line_pointer = save + (input_line_pointer-tmp-2);
if (O_big == exp->X_op)
{
exp->X_add_number = cons_len / CHARS_PER_LITTLENUM;
}
}
else
{
expression (exp);
}
if ((exp->X_op != O_constant) && (exp->X_op != O_big))
{
as_bad ("expression not a constant");
return FALSE;
}
add_to_lit_pool (exp, 0x0, cons_len);
break;
default:
as_bad ("Unknown/unsupported address literal type");
return FALSE;
}
return TRUE;
}
static void
i370_ltorg (ignore)
int ignore ATTRIBUTE_UNUSED;
{
int litsize;
int lit_count = 0;
int biggest_literal_size = 0;
int biggest_align = 0;
char pool_name[20];
if (strncmp (now_seg->name, ".text", 5))
{
if (i370_other_section == undefined_section)
{
as_bad (".ltorg without prior .using in section %s",
now_seg->name);
}
if (i370_other_section != now_seg)
{
as_bad (".ltorg in section %s paired to .using in section %s",
now_seg->name, i370_other_section->name);
}
}
if (! longlong_poolP
&& ! word_poolP
&& ! short_poolP
&& ! byte_poolP)
{
return;
}
lit_count = 0;
while (lit_count < next_literal_pool_place)
{
if (biggest_literal_size < literals[lit_count].size)
biggest_literal_size = literals[lit_count].size;
lit_count ++;
}
if (1 == biggest_literal_size) biggest_align = 0;
else if (2 == biggest_literal_size) biggest_align = 1;
else if (4 == biggest_literal_size) biggest_align = 2;
else if (8 == biggest_literal_size) biggest_align = 3;
else as_bad ("bad alignment of %d bytes in literal pool", biggest_literal_size);
if (0 == biggest_align) biggest_align = 1;
frag_align (biggest_align, 0, 0);
record_alignment (now_seg, biggest_align);
for (litsize=8; litsize; litsize /=2)
{
symbolS *current_poolP = NULL;
switch (litsize)
{
case 8:
current_poolP = longlong_poolP; break;
case 4:
current_poolP = word_poolP; break;
case 2:
current_poolP = short_poolP; break;
case 1:
current_poolP = byte_poolP; break;
default:
as_bad ("bad literal size\n");
}
if (NULL == current_poolP)
continue;
sprintf (pool_name, ".LITP%01d%06d", litsize, lit_pool_num);
symbol_locate (current_poolP, pool_name, now_seg,
(valueT) frag_now_fix (), frag_now);
symbol_table_insert (current_poolP);
lit_count = 0;
while (lit_count < next_literal_pool_place)
{
if (litsize == literals[lit_count].size)
{
#define EMIT_ADDR_CONS_SYMBOLS
#ifdef EMIT_ADDR_CONS_SYMBOLS
if (literals[lit_count].sym_name)
{
symbolS * symP = symbol_make_empty ();
symbol_locate (symP, literals[lit_count].sym_name, now_seg,
(valueT) frag_now_fix (), frag_now);
symbol_table_insert (symP);
}
#endif
emit_expr (&(literals[lit_count].exp), literals[lit_count].size);
}
lit_count ++;
}
}
next_literal_pool_place = 0;
longlong_poolP = NULL;
word_poolP = NULL;
short_poolP = NULL;
byte_poolP = NULL;
lit_pool_num++;
}
#endif
static void
i370_using (ignore)
int ignore ATTRIBUTE_UNUSED;
{
expressionS ex, baseaddr;
int iregno;
char *star;
star = strchr (input_line_pointer, '*');
if (star)
*star = '.';
expression (&baseaddr);
if (star)
*star = '*';
if (O_constant != baseaddr.X_op
&& O_symbol != baseaddr.X_op
&& O_uminus != baseaddr.X_op)
{
as_bad (".using: base address expression illegal or too complex");
}
if (*input_line_pointer != '\0') ++input_line_pointer;
register_name (&ex);
demand_empty_rest_of_line ();
iregno = ex.X_add_number;
if (0 == strncmp (now_seg->name, ".text", 5))
{
i370_using_text_baseaddr = baseaddr;
i370_using_text_regno = iregno;
}
else
{
i370_using_other_baseaddr = baseaddr;
i370_using_other_regno = iregno;
i370_other_section = now_seg;
}
}
static void
i370_drop (ignore)
int ignore ATTRIBUTE_UNUSED;
{
expressionS ex;
int iregno;
register_name (&ex);
demand_empty_rest_of_line ();
iregno = ex.X_add_number;
if (0 == strncmp (now_seg->name, ".text", 5))
{
if (iregno != i370_using_text_regno)
{
as_bad ("droping register %d in section %s does not match using register %d",
iregno, now_seg->name, i370_using_text_regno);
}
i370_using_text_regno = -1;
i370_using_text_baseaddr.X_op = O_absent;
}
else
{
if (iregno != i370_using_other_regno)
{
as_bad ("droping register %d in section %s does not match using register %d",
iregno, now_seg->name, i370_using_other_regno);
}
if (i370_other_section != now_seg)
{
as_bad ("droping register %d in section %s previously used in section %s",
iregno, now_seg->name, i370_other_section->name);
}
i370_using_other_regno = -1;
i370_using_other_baseaddr.X_op = O_absent;
i370_other_section = undefined_section;
}
}
static void
i370_make_relative (expressionS *exx, expressionS *baseaddr)
{
if (O_constant == baseaddr->X_op)
{
exx->X_op = O_symbol;
exx->X_add_number -= baseaddr->X_add_number;
}
else if (O_symbol == baseaddr->X_op)
{
exx->X_op = O_subtract;
exx->X_op_symbol = baseaddr->X_add_symbol;
exx->X_add_number -= baseaddr->X_add_number;
}
else if (O_uminus == baseaddr->X_op)
{
exx->X_op = O_add;
exx->X_op_symbol = baseaddr->X_add_symbol;
exx->X_add_number += baseaddr->X_add_number;
}
else
{
as_bad ("Missing or bad .using directive");
}
}
struct i370_fixup
{
expressionS exp;
int opindex;
bfd_reloc_code_real_type reloc;
};
#define MAX_INSN_FIXUPS (5)
void
md_assemble (str)
char *str;
{
char *s, *opcode_str;
const struct i370_opcode *opcode;
i370_insn_t insn;
const unsigned char *opindex_ptr;
int have_optional_index, have_optional_basereg, have_optional_reg;
int skip_optional_index, skip_optional_basereg, skip_optional_reg;
int use_text=0, use_other=0;
int off_by_one;
struct i370_fixup fixups[MAX_INSN_FIXUPS];
int fc;
char *f;
int i;
#ifdef OBJ_ELF
bfd_reloc_code_real_type reloc;
#endif
for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
;
if (*s != '\0')
*s++ = '\0';
opcode_str = str;
opcode = (const struct i370_opcode *) hash_find (i370_hash, str);
if (opcode == (const struct i370_opcode *) NULL)
{
const struct i370_macro *macro;
assert (i370_macro_hash);
macro = (const struct i370_macro *) hash_find (i370_macro_hash, str);
if (macro == (const struct i370_macro *) NULL)
as_bad ("Unrecognized opcode: `%s'", str);
else
i370_macro (s, macro);
return;
}
insn = opcode->opcode;
str = s;
while (ISSPACE (*str))
++str;
have_optional_index = have_optional_basereg = have_optional_reg = 0;
for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
{
const struct i370_operand *operand;
operand = &i370_operands[*opindex_ptr];
if ((operand->flags & I370_OPERAND_INDEX) != 0)
have_optional_index = 1;
if ((operand->flags & I370_OPERAND_BASE) != 0)
have_optional_basereg = 1;
if ((operand->flags & I370_OPERAND_OPTIONAL) != 0)
have_optional_reg = 1;
}
skip_optional_index = skip_optional_basereg = skip_optional_reg = 0;
if (have_optional_index || have_optional_basereg)
{
unsigned int opcount, nwanted;
if (*str == '\0')
opcount = 0;
else
{
opcount = 1;
s = str;
while ((s = strpbrk (s, ",(=")) != (char *) NULL)
{
++opcount;
++s;
if (',' == *s) ++s;
if ('=' == *s) { ++s; --opcount; }
}
}
nwanted = strlen ((char *) opcode->operands);
if (have_optional_index)
{
if (opcount < nwanted)
skip_optional_index = 1;
if (have_optional_basereg && ((opcount+1) < nwanted))
skip_optional_basereg = 1;
if (have_optional_reg && ((opcount+1) < nwanted))
skip_optional_reg = 1;
}
else
{
if (have_optional_basereg && (opcount < nwanted))
skip_optional_basereg = 1;
if (have_optional_reg && (opcount < nwanted))
skip_optional_reg = 1;
}
}
off_by_one = 0;
if (0 == strcasecmp ("CLC", opcode->name)
|| 0 == strcasecmp ("ED", opcode->name)
|| 0 == strcasecmp ("EDMK", opcode->name)
|| 0 == strcasecmp ("MVC", opcode->name)
|| 0 == strcasecmp ("MVCIN", opcode->name)
|| 0 == strcasecmp ("MVN", opcode->name)
|| 0 == strcasecmp ("MVZ", opcode->name)
|| 0 == strcasecmp ("NC", opcode->name)
|| 0 == strcasecmp ("OC", opcode->name)
|| 0 == strcasecmp ("XC", opcode->name))
off_by_one = 1;
fc = 0;
for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
{
const struct i370_operand *operand;
const char *errmsg;
char *hold;
expressionS ex;
operand = &i370_operands[*opindex_ptr];
errmsg = NULL;
if (skip_optional_index &&
((operand->flags & I370_OPERAND_INDEX) != 0))
{
insn = i370_insert_operand (insn, operand, 0);
continue;
}
if (skip_optional_basereg &&
((operand->flags & I370_OPERAND_BASE) != 0))
{
int basereg = -1;
if (use_text)
{
if (0 == strncmp (now_seg->name, ".text", 5)
|| 0 > i370_using_other_regno)
{
basereg = i370_using_text_regno;
}
else
{
basereg = i370_using_other_regno;
}
}
else if (use_other)
{
if (0 > i370_using_other_regno)
{
basereg = i370_using_text_regno;
}
else
{
basereg = i370_using_other_regno;
}
}
if (0 > basereg)
{
as_bad ("not using any base register");
}
insn = i370_insert_operand (insn, operand, basereg);
continue;
}
if (skip_optional_reg
&& ((operand->flags & I370_OPERAND_OPTIONAL) != 0))
{
insn = i370_insert_operand (insn, operand, 0);
continue;
}
hold = input_line_pointer;
input_line_pointer = str;
if ((operand->flags & I370_OPERAND_GPR) != 0)
{
if (skip_optional_index && (',' == *input_line_pointer))
{
*input_line_pointer = ' ';
input_line_pointer ++;
}
if (! register_name (&ex))
{
as_bad ("expecting a register for operand %d",
opindex_ptr - opcode->operands + 1);
}
}
else if (i370_addr_cons (&ex))
use_other=1;
else if (i370_addr_offset (&ex))
use_text=1;
else expression (&ex);
str = input_line_pointer;
input_line_pointer = hold;
if (off_by_one && (0 == strcasecmp ("SS L", operand->name)))
{
ex.X_add_number --;
}
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 = i370_insert_operand (insn, operand, ex.X_add_number);
}
else if (ex.X_op == O_constant)
{
#ifdef OBJ_ELF
char *orig_str = str;
if ((reloc = i370_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
switch (reloc)
{
default:
str = orig_str;
break;
case BFD_RELOC_LO16:
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));
break;
}
#endif
insn = i370_insert_operand (insn, operand, ex.X_add_number);
}
#ifdef OBJ_ELF
else if ((reloc = i370_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
{
as_tsktsk ("md_assemble(): suffixed relocations not supported\n");
if (fc >= MAX_INSN_FIXUPS)
as_fatal ("too many fixups");
fixups[fc].exp = ex;
fixups[fc].opindex = 0;
fixups[fc].reloc = reloc;
++fc;
}
#endif
else
{
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 ((')' == *str) && (',' == *(str+1)))
++str;
if (*str != '\0')
++str;
}
while (ISSPACE (*str))
++str;
if (*str != '\0')
as_bad ("junk at end of line: `%s'", str);
f = frag_more (opcode->len);
if (4 >= opcode->len)
{
md_number_to_chars (f, insn.i[0], opcode->len);
}
else
{
md_number_to_chars (f, insn.i[0], 4);
if (6 == opcode->len)
{
md_number_to_chars ((f+4), ((insn.i[1])>>16), 2);
}
else
{
as_bad ("Internal Error: bad instruction length");
md_number_to_chars ((f+4), insn.i[1], opcode->len -4);
}
}
for (i = 0; i < fc; i++)
{
const struct i370_operand *operand;
operand = &i370_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;
fixS *fixP;
if (!reloc_howto)
abort ();
size = bfd_get_reloc_size (reloc_howto);
if (size < 1 || size > 4)
abort ();
printf (" gwana doo fixup %d \n", i);
fixP = fix_new_exp (frag_now, f - frag_now->fr_literal, size,
&fixups[i].exp, reloc_howto->pc_relative,
fixups[i].reloc);
switch (fixups[i].reloc)
{
case BFD_RELOC_16_GOTOFF:
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, opcode->len,
&fixups[i].exp,
(operand->flags & I370_OPERAND_RELATIVE) != 0,
((bfd_reloc_code_real_type)
(fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
}
}
}
static void
i370_macro (str, macro)
char *str;
const struct i370_macro *macro;
{
char *operands[10];
unsigned int count;
char *s;
unsigned int len;
const char *format;
int arg;
char *send;
char *complete;
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;
}
len = 0;
format = macro->format;
while (*format != '\0')
{
if (*format != '%')
{
++len;
++format;
}
else
{
arg = strtol (format + 1, &send, 10);
know (send != format && arg >= 0 && (unsigned) arg < count);
len += strlen (operands[arg]);
format = send;
}
}
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';
md_assemble (complete);
}
static void
i370_byte (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (*input_line_pointer != '\"')
{
cons (1);
return;
}
++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 ();
}
static void
i370_tc (ignore)
int ignore ATTRIBUTE_UNUSED;
{
while (is_part_of_name (*input_line_pointer)
|| *input_line_pointer == '['
|| *input_line_pointer == ']'
|| *input_line_pointer == '{'
|| *input_line_pointer == '}')
++input_line_pointer;
frag_align (2, 0, 0);
record_alignment (now_seg, 2);
if (*input_line_pointer != ',')
demand_empty_rest_of_line ();
else
{
++input_line_pointer;
cons (4);
}
}
char *
md_atof (type, litp, sizep)
int type;
char *litp;
int *sizep;
{
int prec;
LITTLENUM_TYPE words[4];
char *t;
int i;
switch (type)
{
case 'f':
case 'E':
type = 'f';
prec = 2;
break;
case 'd':
case 'D':
type = '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;
for (i = 0; i < prec; i++)
{
md_number_to_chars (litp, (valueT) words[i], 2);
litp += 2;
}
return NULL;
}
void
md_number_to_chars (buf, val, n)
char *buf;
valueT val;
int n;
{
number_to_chars_bigendian (buf, val, n);
}
valueT
md_section_align (seg, addr)
asection *seg;
valueT addr;
{
int align = bfd_get_section_alignment (stdoutput, seg);
return (addr + (1 << align) - 1) & (-1 << align);
}
int
md_estimate_size_before_relax (fragp, seg)
fragS *fragp ATTRIBUTE_UNUSED;
asection *seg ATTRIBUTE_UNUSED;
{
abort ();
return 0;
}
void
md_convert_frag (abfd, sec, fragp)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
fragS *fragp ATTRIBUTE_UNUSED;
{
abort ();
}
symbolS *
md_undefined_symbol (name)
char *name ATTRIBUTE_UNUSED;
{
return 0;
}
long
md_pcrel_from_section (fixp, sec)
fixS *fixp;
segT sec ATTRIBUTE_UNUSED;
{
return fixp->fx_frag->fr_address + fixp->fx_where;
}
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT * valP;
segT seg;
{
valueT value = * valP;
if (fixP->fx_addsy != NULL)
{
#ifdef DEBUG
printf ("\nmd_apply_fix3: symbol %s at 0x%x (%s:%d) val=0x%x addend=0x%x\n",
S_GET_NAME (fixP->fx_addsy),
fixP->fx_frag->fr_address + fixP->fx_where,
fixP->fx_file, fixP->fx_line,
S_GET_VALUE (fixP->fx_addsy), value);
#endif
}
else
fixP->fx_done = 1;
if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
{
int opindex;
const struct i370_operand *operand;
char *where;
i370_insn_t insn;
opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
operand = &i370_operands[opindex];
#ifdef DEBUG
printf ("\nmd_apply_fix3: fixup operand %s at 0x%x in %s:%d addend=0x%x\n",
operand->name,
fixP->fx_frag->fr_address + fixP->fx_where,
fixP->fx_file, fixP->fx_line,
value);
#endif
where = fixP->fx_frag->fr_literal + fixP->fx_where;
insn.i[0] = bfd_getb32 ((unsigned char *) where);
if (6 <= fixP->fx_size)
insn.i[1] = bfd_getb32 (((unsigned char *) where)+4);
insn = i370_insert_operand (insn, operand, (offsetT) value);
bfd_putb32 ((bfd_vma) insn.i[0], (unsigned char *) where);
if (6 <= fixP->fx_size)
bfd_putb32 ((bfd_vma) insn.i[1], (((unsigned char *) where)+4));
fixP->fx_done = 1;
if (fixP->fx_done)
return;
{
char *sfile;
unsigned int sline;
if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
as_bad_where (fixP->fx_file, fixP->fx_line,
"unresolved expression that must be resolved");
else
as_bad_where (fixP->fx_file, fixP->fx_line,
"unsupported relocation type");
fixP->fx_done = 1;
return;
}
}
else
{
#ifdef OBJ_ELF
i370_elf_validate_fix (fixP, seg);
#endif
#ifdef DEBUG
printf ("md_apply_fix3: reloc case %d in segment %s %s:%d\n",
fixP->fx_r_type, segment_name (seg), fixP->fx_file, fixP->fx_line);
printf ("\tcurrent fixup value is 0x%x \n", value);
#endif
switch (fixP->fx_r_type)
{
case BFD_RELOC_32:
case BFD_RELOC_CTOR:
if (fixP->fx_pcrel)
fixP->fx_r_type = BFD_RELOC_32_PCREL;
case BFD_RELOC_RVA:
case BFD_RELOC_32_PCREL:
case BFD_RELOC_32_BASEREL:
#ifdef DEBUG
printf ("\t32 bit relocation at 0x%x\n",
fixP->fx_frag->fr_address + fixP->fx_where);
#endif
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 4);
break;
case BFD_RELOC_LO16:
case BFD_RELOC_16:
if (fixP->fx_pcrel)
as_bad_where (fixP->fx_file, fixP->fx_line,
"cannot emit PC relative %s relocation%s%s",
bfd_get_reloc_code_name (fixP->fx_r_type),
fixP->fx_addsy != NULL ? " against " : "",
(fixP->fx_addsy != NULL
? S_GET_NAME (fixP->fx_addsy)
: ""));
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 2);
break;
case BFD_RELOC_HI16:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value >> 16, 2);
break;
case BFD_RELOC_HI16_S:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
(value + 0x8000) >> 16, 2);
break;
case BFD_RELOC_8:
if (fixP->fx_pcrel)
abort ();
md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
value, 1);
break;
default:
fprintf (stderr,
"Gas failure, reloc value %d\n", fixP->fx_r_type);
fflush (stderr);
abort ();
}
}
fixP->fx_addnumber = value;
}
arelent *
tc_gen_reloc (seg, fixp)
asection *seg ATTRIBUTE_UNUSED;
fixS *fixp;
{
arelent *reloc;
reloc = (arelent *) xmalloc (sizeof (arelent));
reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
if (reloc->howto == (reloc_howto_type *) NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
"reloc %d not supported by object file format", (int)fixp->fx_r_type);
return NULL;
}
reloc->addend = fixp->fx_addnumber;
#ifdef DEBUG
printf ("\ngen_reloc(): sym %s (%s:%d) at addr 0x%x addend=0x%x\n",
fixp->fx_addsy->bsym->name,
fixp->fx_file, fixp->fx_line,
reloc->address, reloc->addend);
#endif
return reloc;
}