#include <stdio.h>
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "struc-symbol.h"
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
#include "opcode/s390.h"
#include "elf/s390.h"
#ifndef DEFAULT_ARCH
#define DEFAULT_ARCH "s390"
#endif
static char *default_arch = DEFAULT_ARCH;
static int s390_arch_size = 0;
static unsigned int current_mode_mask = 0;
static unsigned int current_cpu = -1U;
#ifndef TARGET_REG_NAMES_P
#define TARGET_REG_NAMES_P TRUE
#endif
static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
static bfd_boolean warn_areg_zero = FALSE;
const char comment_chars[] = "#";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";
int s390_cie_data_alignment;
static void s390_byte PARAMS ((int));
static void s390_elf_cons PARAMS ((int));
static void s390_bss PARAMS ((int));
static void s390_insn PARAMS ((int));
static void s390_literals PARAMS ((int));
const pseudo_typeS md_pseudo_table[] =
{
{ "align", s_align_bytes, 0 },
{ "bss", s390_bss, 0 },
{ "insn", s390_insn, 0 },
{ "byte", s390_byte, 0 },
{ "short", s390_elf_cons, 2 },
{ "long", s390_elf_cons, 4 },
{ "quad", s390_elf_cons, 8 },
{ "ltorg", s390_literals, 0 },
{ "string", stringer, 2 },
{ NULL, NULL, 0 }
};
struct pd_reg
{
char *name;
int value;
};
static const struct pd_reg pre_defined_registers[] =
{
{ "a0", 0 },
{ "a1", 1 },
{ "a10", 10 },
{ "a11", 11 },
{ "a12", 12 },
{ "a13", 13 },
{ "a14", 14 },
{ "a15", 15 },
{ "a2", 2 },
{ "a3", 3 },
{ "a4", 4 },
{ "a5", 5 },
{ "a6", 6 },
{ "a7", 7 },
{ "a8", 8 },
{ "a9", 9 },
{ "c0", 0 },
{ "c1", 1 },
{ "c10", 10 },
{ "c11", 11 },
{ "c12", 12 },
{ "c13", 13 },
{ "c14", 14 },
{ "c15", 15 },
{ "c2", 2 },
{ "c3", 3 },
{ "c4", 4 },
{ "c5", 5 },
{ "c6", 6 },
{ "c7", 7 },
{ "c8", 8 },
{ "c9", 9 },
{ "f0", 0 },
{ "f1", 1 },
{ "f10", 10 },
{ "f11", 11 },
{ "f12", 12 },
{ "f13", 13 },
{ "f14", 14 },
{ "f15", 15 },
{ "f2", 2 },
{ "f3", 3 },
{ "f4", 4 },
{ "f5", 5 },
{ "f6", 6 },
{ "f7", 7 },
{ "f8", 8 },
{ "f9", 9 },
{ "lit", 13 },
{ "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 },
{ "sp", 15 },
};
#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 *));
static bfd_boolean register_name PARAMS ((expressionS *));
static void init_default_arch PARAMS ((void));
static void s390_insert_operand
PARAMS ((unsigned char *, const struct s390_operand *, offsetT, char *,
unsigned int));
static char *md_gather_operands
PARAMS ((char *, unsigned char *, const struct s390_opcode *));
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
return FALSE;
c = get_symbol_end ();
reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
*input_line_pointer = c;
if (reg_number >= 0)
{
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 struct hash_control *s390_opformat_hash;
static struct hash_control *s390_opcode_hash;
static flagword s390_flags = 0;
symbolS *GOT_symbol;
#ifndef WORKING_DOT_WORD
int md_short_jump_size = 4;
int md_long_jump_size = 4;
#endif
const char *md_shortopts = "A:m:kVQ:";
struct option md_longopts[] = {
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
static void
init_default_arch ()
{
if (strcmp (default_arch, "s390") == 0)
{
if (s390_arch_size == 0)
s390_arch_size = 32;
}
else if (strcmp (default_arch, "s390x") == 0)
{
if (s390_arch_size == 0)
s390_arch_size = 64;
}
else
as_fatal ("Invalid default architecture, broken assembler.");
if (current_mode_mask == 0)
{
if (s390_arch_size == 32)
current_mode_mask = 1 << S390_OPCODE_ESA;
else
current_mode_mask = 1 << S390_OPCODE_ZARCH;
}
if (current_cpu == -1U)
{
if (current_mode_mask == (1 << S390_OPCODE_ESA))
current_cpu = S390_OPCODE_G5;
else
current_cpu = S390_OPCODE_Z900;
}
}
const char *
s390_target_format ()
{
init_default_arch ();
return s390_arch_size == 64 ? "elf64-s390" : "elf32-s390";
}
int
md_parse_option (c, arg)
int c;
char *arg;
{
switch (c)
{
case 'k':
break;
case 'm':
if (arg != NULL && strcmp (arg, "regnames") == 0)
reg_names_p = TRUE;
else if (arg != NULL && strcmp (arg, "no-regnames") == 0)
reg_names_p = FALSE;
else if (arg != NULL && strcmp (arg, "warn-areg-zero") == 0)
warn_areg_zero = TRUE;
else if (arg != NULL && strcmp (arg, "31") == 0)
s390_arch_size = 32;
else if (arg != NULL && strcmp (arg, "64") == 0)
s390_arch_size = 64;
else if (arg != NULL && strcmp (arg, "esa") == 0)
current_mode_mask = 1 << S390_OPCODE_ESA;
else if (arg != NULL && strcmp (arg, "zarch") == 0)
current_mode_mask = 1 << S390_OPCODE_ZARCH;
else if (arg != NULL && strncmp (arg, "arch=", 5) == 0)
{
if (strcmp (arg + 5, "g5") == 0)
current_cpu = S390_OPCODE_G5;
else if (strcmp (arg + 5, "g6") == 0)
current_cpu = S390_OPCODE_G6;
else if (strcmp (arg + 5, "z900") == 0)
current_cpu = S390_OPCODE_Z900;
else if (strcmp (arg + 5, "z990") == 0)
current_cpu = S390_OPCODE_Z990;
else
{
as_bad (_("invalid switch -m%s"), arg);
return 0;
}
}
else
{
as_bad (_("invalid switch -m%s"), arg);
return 0;
}
break;
case 'A':
if (arg != NULL && strcmp (arg, "esa") == 0)
current_cpu = S390_OPCODE_G5;
else if (arg != NULL && strcmp (arg, "esame") == 0)
current_cpu = S390_OPCODE_Z900;
else
as_bad ("invalid architecture -A%s", arg);
break;
case 'V':
print_version_id ();
break;
case 'Q':
break;
default:
return 0;
}
return 1;
}
void
md_show_usage (stream)
FILE *stream;
{
fprintf (stream, _("\
S390 options:\n\
-mregnames Allow symbolic names for registers\n\
-mwarn-areg-zero Warn about zero base/index registers\n\
-mno-regnames Do not allow symbolic names for registers\n\
-m31 Set file format to 31 bit format\n\
-m64 Set file format to 64 bit format\n"));
fprintf (stream, _("\
-V print assembler version number\n\
-Qy, -Qn ignored\n"));
}
void
md_begin ()
{
register const struct s390_opcode *op;
const struct s390_opcode *op_end;
bfd_boolean dup_insn = FALSE;
const char *retval;
if (s390_arch_size == 64 && current_cpu < S390_OPCODE_Z900)
as_warn ("The 64 bit file format is used without esame instructions.");
s390_cie_data_alignment = -s390_arch_size / 8;
if (s390_flags)
bfd_set_private_flags (stdoutput, s390_flags);
s390_opformat_hash = hash_new ();
op_end = s390_opformats + s390_num_opformats;
for (op = s390_opformats; op < op_end; op++)
{
retval = hash_insert (s390_opformat_hash, op->name, (PTR) op);
if (retval != (const char *) NULL)
{
as_bad (_("Internal assembler error for instruction format %s"),
op->name);
dup_insn = TRUE;
}
}
s390_opcode_hash = hash_new ();
op_end = s390_opcodes + s390_num_opcodes;
for (op = s390_opcodes; op < op_end; op++)
if (op->min_cpu <= current_cpu)
{
retval = hash_insert (s390_opcode_hash, op->name, (PTR) op);
if (retval != (const char *) NULL)
{
as_bad (_("Internal assembler error for instruction %s"),
op->name);
dup_insn = TRUE;
}
while (op < op_end - 1 && strcmp (op->name, op[1].name) == 0)
op++;
}
if (dup_insn)
abort ();
record_alignment (text_section, 2);
record_alignment (data_section, 2);
record_alignment (bss_section, 2);
}
void
s390_md_end ()
{
if (s390_arch_size == 64)
bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_64);
else
bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_31);
}
static void
s390_insert_operand (insn, operand, val, file, line)
unsigned char *insn;
const struct s390_operand *operand;
offsetT val;
char *file;
unsigned int line;
{
addressT uval;
int offset;
if (operand->flags & (S390_OPERAND_SIGNED|S390_OPERAND_PCREL))
{
offsetT min, max;
max = ((offsetT) 1 << (operand->bits - 1)) - 1;
min = - ((offsetT) 1 << (operand->bits - 1));
if (operand->flags & S390_OPERAND_PCREL)
val >>= 1;
if (val < min || val > max)
{
const char *err =
"operand out of range (%s not between %ld and %ld)";
char buf[100];
if (operand->flags & S390_OPERAND_PCREL)
{
val <<= 1;
min <<= 1;
max <<= 1;
}
sprint_value (buf, val);
if (file == (char *) NULL)
as_bad (err, buf, (int) min, (int) max);
else
as_bad_where (file, line, err, buf, (int) min, (int) max);
return;
}
uval = (addressT) val & ((((addressT) 1 << (operand->bits-1)) << 1) - 1);
if (operand->bits == 20 && operand->shift == 20)
uval = (uval >> 12) | ((uval & 0xfff) << 8);
}
else
{
addressT min, max;
max = (((addressT) 1 << (operand->bits - 1)) << 1) - 1;
min = (offsetT) 0;
uval = (addressT) val;
if (operand->flags & S390_OPERAND_LENGTH)
uval--;
if (uval < min || uval > max)
{
if (operand->flags & S390_OPERAND_LENGTH)
{
uval++;
min++;
max++;
}
as_bad_value_out_of_range (_("operand"), uval, (offsetT) min, (offsetT) max, file, line);
return;
}
}
offset = operand->shift + operand->bits;
uval <<= (-offset) & 7;
insn += (offset - 1) / 8;
while (uval != 0)
{
*insn-- |= uval;
uval >>= 8;
}
}
struct map_tls
{
char *string;
int length;
bfd_reloc_code_real_type reloc;
};
static bfd_reloc_code_real_type s390_tls_suffix
PARAMS ((char **, expressionS *));
static bfd_reloc_code_real_type
s390_tls_suffix (str_p, exp_p)
char **str_p;
expressionS *exp_p;
{
static struct map_tls mapping[] =
{
{ "tls_load", 8, BFD_RELOC_390_TLS_LOAD },
{ "tls_gdcall", 10, BFD_RELOC_390_TLS_GDCALL },
{ "tls_ldcall", 10, BFD_RELOC_390_TLS_LDCALL },
{ NULL, 0, BFD_RELOC_UNUSED }
};
struct map_tls *ptr;
char *orig_line;
char *str;
char *ident;
int len;
str = *str_p;
if (*str++ != ':')
return BFD_RELOC_UNUSED;
ident = str;
while (ISIDNUM (*str))
str++;
len = str - ident;
if (*str++ != ':')
return BFD_RELOC_UNUSED;
orig_line = input_line_pointer;
input_line_pointer = str;
expression (exp_p);
str = input_line_pointer;
if (&input_line_pointer != str_p)
input_line_pointer = orig_line;
if (exp_p->X_op != O_symbol)
return BFD_RELOC_UNUSED;
for (ptr = &mapping[0]; ptr->length > 0; ptr++)
if (len == ptr->length
&& strncasecmp (ident, ptr->string, ptr->length) == 0)
{
*str_p = str;
return ptr->reloc;
}
return BFD_RELOC_UNUSED;
}
typedef enum
{
ELF_SUFFIX_NONE = 0,
ELF_SUFFIX_GOT,
ELF_SUFFIX_PLT,
ELF_SUFFIX_GOTENT,
ELF_SUFFIX_GOTOFF,
ELF_SUFFIX_GOTPLT,
ELF_SUFFIX_PLTOFF,
ELF_SUFFIX_TLS_GD,
ELF_SUFFIX_TLS_GOTIE,
ELF_SUFFIX_TLS_IE,
ELF_SUFFIX_TLS_LDM,
ELF_SUFFIX_TLS_LDO,
ELF_SUFFIX_TLS_LE
}
elf_suffix_type;
struct map_bfd
{
char *string;
int length;
elf_suffix_type suffix;
};
static elf_suffix_type s390_elf_suffix PARAMS ((char **, expressionS *));
static int s390_exp_compare PARAMS ((expressionS *exp1, expressionS *exp2));
static elf_suffix_type s390_lit_suffix
PARAMS ((char **, expressionS *, elf_suffix_type));
static elf_suffix_type
s390_elf_suffix (str_p, exp_p)
char **str_p;
expressionS *exp_p;
{
static struct map_bfd mapping[] =
{
{ "got", 3, ELF_SUFFIX_GOT },
{ "got12", 5, ELF_SUFFIX_GOT },
{ "plt", 3, ELF_SUFFIX_PLT },
{ "gotent", 6, ELF_SUFFIX_GOTENT },
{ "gotoff", 6, ELF_SUFFIX_GOTOFF },
{ "gotplt", 6, ELF_SUFFIX_GOTPLT },
{ "pltoff", 6, ELF_SUFFIX_PLTOFF },
{ "tlsgd", 5, ELF_SUFFIX_TLS_GD },
{ "gotntpoff", 9, ELF_SUFFIX_TLS_GOTIE },
{ "indntpoff", 9, ELF_SUFFIX_TLS_IE },
{ "tlsldm", 6, ELF_SUFFIX_TLS_LDM },
{ "dtpoff", 6, ELF_SUFFIX_TLS_LDO },
{ "ntpoff", 6, ELF_SUFFIX_TLS_LE },
{ NULL, 0, ELF_SUFFIX_NONE }
};
struct map_bfd *ptr;
char *str = *str_p;
char *ident;
int len;
if (*str++ != '@')
return ELF_SUFFIX_NONE;
ident = str;
while (ISALNUM (*str))
str++;
len = str - ident;
for (ptr = &mapping[0]; ptr->length > 0; ptr++)
if (len == ptr->length
&& strncasecmp (ident, ptr->string, ptr->length) == 0)
{
if (exp_p->X_add_number != 0)
as_warn (_("identifier+constant@%s means identifier@%s+constant"),
ptr->string, ptr->string);
if (*str == '-' || *str == '+')
{
char *orig_line = input_line_pointer;
expressionS new_exp;
input_line_pointer = str;
expression (&new_exp);
switch (new_exp.X_op)
{
case O_constant:
exp_p->X_add_number += new_exp.X_add_number;
str = input_line_pointer;
break;
case O_symbol:
exp_p->X_add_number += new_exp.X_add_number;
exp_p->X_op_symbol = new_exp.X_add_symbol;
exp_p->X_op = O_add;
str = input_line_pointer;
break;
case O_uminus:
exp_p->X_add_number += new_exp.X_add_number;
exp_p->X_op_symbol = new_exp.X_add_symbol;
exp_p->X_op = O_subtract;
str = input_line_pointer;
break;
default:
break;
}
if (&input_line_pointer != str_p)
input_line_pointer = orig_line;
}
*str_p = str;
return ptr->suffix;
}
return BFD_RELOC_UNUSED;
}
struct s390_lpe
{
struct s390_lpe *next;
expressionS ex;
FLONUM_TYPE floatnum;
LITTLENUM_TYPE bignum[4];
int nbytes;
bfd_reloc_code_real_type reloc;
symbolS *sym;
};
static struct s390_lpe *lpe_free_list = NULL;
static struct s390_lpe *lpe_list = NULL;
static struct s390_lpe *lpe_list_tail = NULL;
static symbolS *lp_sym = NULL;
static int lp_count = 0;
static int lpe_count = 0;
static int
s390_exp_compare (exp1, exp2)
expressionS *exp1;
expressionS *exp2;
{
if (exp1->X_op != exp2->X_op)
return 0;
switch (exp1->X_op)
{
case O_constant:
case O_register:
return exp1->X_add_number == exp2->X_add_number;
case O_big:
as_bad (_("Can't handle O_big in s390_exp_compare"));
case O_symbol:
case O_symbol_rva:
case O_uminus:
case O_bit_not:
case O_logical_not:
return (exp1->X_add_symbol == exp2->X_add_symbol)
&& (exp1->X_add_number == exp2->X_add_number);
case O_multiply:
case O_divide:
case O_modulus:
case O_left_shift:
case O_right_shift:
case O_bit_inclusive_or:
case O_bit_or_not:
case O_bit_exclusive_or:
case O_bit_and:
case O_add:
case O_subtract:
case O_eq:
case O_ne:
case O_lt:
case O_le:
case O_ge:
case O_gt:
case O_logical_and:
case O_logical_or:
return (exp1->X_add_symbol == exp2->X_add_symbol)
&& (exp1->X_op_symbol == exp2->X_op_symbol)
&& (exp1->X_add_number == exp2->X_add_number);
default:
return 0;
}
}
static elf_suffix_type
s390_lit_suffix (str_p, exp_p, suffix)
char **str_p;
expressionS *exp_p;
elf_suffix_type suffix;
{
bfd_reloc_code_real_type reloc;
char tmp_name[64];
char *str = *str_p;
char *ident;
struct s390_lpe *lpe;
int nbytes, len;
if (*str++ != ':')
return suffix;
ident = str;
while (ISALNUM (*str))
str++;
len = str - ident;
if (len != 4 || strncasecmp (ident, "lit", 3) != 0
|| (ident[3]!='1' && ident[3]!='2' && ident[3]!='4' && ident[3]!='8'))
return suffix;
nbytes = ident[3] - '0';
reloc = BFD_RELOC_UNUSED;
if (suffix == ELF_SUFFIX_GOT)
{
if (nbytes == 2)
reloc = BFD_RELOC_390_GOT16;
else if (nbytes == 4)
reloc = BFD_RELOC_32_GOT_PCREL;
else if (nbytes == 8)
reloc = BFD_RELOC_390_GOT64;
}
else if (suffix == ELF_SUFFIX_PLT)
{
if (nbytes == 4)
reloc = BFD_RELOC_390_PLT32;
else if (nbytes == 8)
reloc = BFD_RELOC_390_PLT64;
}
if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
as_bad (_("Invalid suffix for literal pool entry"));
if (exp_p->X_op == O_big)
{
for (lpe = lpe_list; lpe != NULL; lpe = lpe->next)
{
if (lpe->ex.X_op == O_big)
{
if (exp_p->X_add_number <= 0 && lpe->ex.X_add_number <= 0)
{
if (memcmp (&generic_floating_point_number, &lpe->floatnum,
sizeof (FLONUM_TYPE)) == 0)
break;
}
else if (exp_p->X_add_number == lpe->ex.X_add_number)
{
if (memcmp (generic_bignum, lpe->bignum,
sizeof (LITTLENUM_TYPE)*exp_p->X_add_number) == 0)
break;
}
}
}
}
else
{
for (lpe = lpe_list; lpe != NULL; lpe = lpe->next)
if (lpe->nbytes == nbytes && lpe->reloc == reloc
&& s390_exp_compare (exp_p, &lpe->ex) != 0)
break;
}
if (lpe == NULL)
{
if (lpe_free_list != NULL)
{
lpe = lpe_free_list;
lpe_free_list = lpe_free_list->next;
}
else
{
lpe = (struct s390_lpe *) xmalloc (sizeof (struct s390_lpe));
}
lpe->ex = *exp_p;
if (exp_p->X_op == O_big)
{
if (exp_p->X_add_number <= 0)
lpe->floatnum = generic_floating_point_number;
else if (exp_p->X_add_number <= 4)
memcpy (lpe->bignum, generic_bignum,
exp_p->X_add_number * sizeof (LITTLENUM_TYPE));
else
as_bad (_("Big number is too big"));
}
lpe->nbytes = nbytes;
lpe->reloc = reloc;
if (lp_sym == NULL)
{
sprintf (tmp_name, ".L\001%i", lp_count);
lp_sym = symbol_make (tmp_name);
}
sprintf (tmp_name, ".L\001%i\002%i", lp_count, lpe_count);
lpe_count++;
lpe->sym = symbol_make (tmp_name);
lpe->next = NULL;
if (lpe_list_tail != NULL)
{
lpe_list_tail->next = lpe;
lpe_list_tail = lpe;
}
else
lpe_list = lpe_list_tail = lpe;
}
exp_p->X_add_symbol = lpe->sym;
exp_p->X_op_symbol = lp_sym;
exp_p->X_op = O_subtract;
exp_p->X_add_number = 0;
*str_p = str;
return ELF_SUFFIX_NONE;
}
static void
s390_elf_cons (nbytes)
register int nbytes;
{
expressionS exp;
elf_suffix_type suffix;
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
do
{
expression (&exp);
if (exp.X_op == O_symbol
&& *input_line_pointer == '@'
&& (suffix = s390_elf_suffix (&input_line_pointer, &exp)) != ELF_SUFFIX_NONE)
{
bfd_reloc_code_real_type reloc;
reloc_howto_type *reloc_howto;
int size;
char *where;
if (nbytes == 2)
{
static bfd_reloc_code_real_type tab2[] =
{
BFD_RELOC_UNUSED,
BFD_RELOC_390_GOT16,
BFD_RELOC_UNUSED,
BFD_RELOC_UNUSED,
BFD_RELOC_16_GOTOFF,
BFD_RELOC_UNUSED,
BFD_RELOC_390_PLTOFF16,
BFD_RELOC_UNUSED,
BFD_RELOC_UNUSED,
BFD_RELOC_UNUSED,
BFD_RELOC_UNUSED,
BFD_RELOC_UNUSED,
BFD_RELOC_UNUSED
};
reloc = tab2[suffix];
}
else if (nbytes == 4)
{
static bfd_reloc_code_real_type tab4[] =
{
BFD_RELOC_UNUSED,
BFD_RELOC_32_GOT_PCREL,
BFD_RELOC_390_PLT32,
BFD_RELOC_UNUSED,
BFD_RELOC_32_GOTOFF,
BFD_RELOC_390_GOTPLT32,
BFD_RELOC_390_PLTOFF32,
BFD_RELOC_390_TLS_GD32,
BFD_RELOC_390_TLS_GOTIE32,
BFD_RELOC_390_TLS_IE32,
BFD_RELOC_390_TLS_LDM32,
BFD_RELOC_390_TLS_LDO32,
BFD_RELOC_390_TLS_LE32
};
reloc = tab4[suffix];
}
else if (nbytes == 8)
{
static bfd_reloc_code_real_type tab8[] =
{
BFD_RELOC_UNUSED,
BFD_RELOC_390_GOT64,
BFD_RELOC_390_PLT64,
BFD_RELOC_UNUSED,
BFD_RELOC_390_GOTOFF64,
BFD_RELOC_390_GOTPLT64,
BFD_RELOC_390_PLTOFF64,
BFD_RELOC_390_TLS_GD64,
BFD_RELOC_390_TLS_GOTIE64,
BFD_RELOC_390_TLS_IE64,
BFD_RELOC_390_TLS_LDM64,
BFD_RELOC_390_TLS_LDO64,
BFD_RELOC_390_TLS_LE64
};
reloc = tab8[suffix];
}
else
reloc = BFD_RELOC_UNUSED;
if (reloc != BFD_RELOC_UNUSED
&& (reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc)))
{
size = bfd_get_reloc_size (reloc_howto);
if (size > nbytes)
as_bad (_("%s relocations do not fit in %d bytes"),
reloc_howto->name, nbytes);
where = frag_more (nbytes);
md_number_to_chars (where, 0, size);
fix_new_exp (frag_now, where - frag_now->fr_literal,
size, &exp, FALSE, reloc);
}
else
as_bad (_("relocation not applicable"));
}
else
emit_expr (&exp, (unsigned int) nbytes);
}
while (*input_line_pointer++ == ',');
input_line_pointer--;
demand_empty_rest_of_line ();
}
struct s390_fixup
{
expressionS exp;
int opindex;
bfd_reloc_code_real_type reloc;
};
#define MAX_INSN_FIXUPS (4)
static char *
md_gather_operands (str, insn, opcode)
char *str;
unsigned char *insn;
const struct s390_opcode *opcode;
{
struct s390_fixup fixups[MAX_INSN_FIXUPS];
const struct s390_operand *operand;
const unsigned char *opindex_ptr;
expressionS ex;
elf_suffix_type suffix;
bfd_reloc_code_real_type reloc;
int skip_optional;
int parentheses;
char *f;
int fc, i;
while (ISSPACE (*str))
str++;
parentheses = 0;
skip_optional = 0;
fc = 0;
for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
{
char *hold;
operand = s390_operands + *opindex_ptr;
if (skip_optional && (operand->flags & S390_OPERAND_INDEX))
{
skip_optional = 0;
continue;
}
hold = input_line_pointer;
input_line_pointer = str;
if (! register_name (&ex))
expression (&ex);
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 || ex.X_op == O_constant)
{
s390_lit_suffix (&str, &ex, ELF_SUFFIX_NONE);
if (ex.X_op != O_register && ex.X_op != O_constant)
{
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;
}
else
{
if ((operand->flags & S390_OPERAND_INDEX)
&& ex.X_add_number == 0
&& warn_areg_zero)
as_warn ("index register specified but zero");
if ((operand->flags & S390_OPERAND_BASE)
&& ex.X_add_number == 0
&& warn_areg_zero)
as_warn ("base register specified but zero");
s390_insert_operand (insn, operand, ex.X_add_number, NULL, 0);
}
}
else
{
suffix = s390_elf_suffix (&str, &ex);
suffix = s390_lit_suffix (&str, &ex, suffix);
reloc = BFD_RELOC_UNUSED;
if (suffix == ELF_SUFFIX_GOT)
{
if ((operand->flags & S390_OPERAND_DISP) &&
(operand->bits == 12))
reloc = BFD_RELOC_390_GOT12;
else if ((operand->flags & S390_OPERAND_DISP) &&
(operand->bits == 20))
reloc = BFD_RELOC_390_GOT20;
else if ((operand->flags & S390_OPERAND_SIGNED)
&& (operand->bits == 16))
reloc = BFD_RELOC_390_GOT16;
else if ((operand->flags & S390_OPERAND_PCREL)
&& (operand->bits == 32))
reloc = BFD_RELOC_390_GOTENT;
}
else if (suffix == ELF_SUFFIX_PLT)
{
if ((operand->flags & S390_OPERAND_PCREL)
&& (operand->bits == 16))
reloc = BFD_RELOC_390_PLT16DBL;
else if ((operand->flags & S390_OPERAND_PCREL)
&& (operand->bits == 32))
reloc = BFD_RELOC_390_PLT32DBL;
}
else if (suffix == ELF_SUFFIX_GOTENT)
{
if ((operand->flags & S390_OPERAND_PCREL)
&& (operand->bits == 32))
reloc = BFD_RELOC_390_GOTENT;
}
else if (suffix == ELF_SUFFIX_GOTOFF)
{
if ((operand->flags & S390_OPERAND_SIGNED)
&& (operand->bits == 16))
reloc = BFD_RELOC_16_GOTOFF;
}
else if (suffix == ELF_SUFFIX_PLTOFF)
{
if ((operand->flags & S390_OPERAND_SIGNED)
&& (operand->bits == 16))
reloc = BFD_RELOC_390_PLTOFF16;
}
else if (suffix == ELF_SUFFIX_GOTPLT)
{
if ((operand->flags & S390_OPERAND_DISP)
&& (operand->bits == 12))
reloc = BFD_RELOC_390_GOTPLT12;
else if ((operand->flags & S390_OPERAND_SIGNED)
&& (operand->bits == 16))
reloc = BFD_RELOC_390_GOTPLT16;
else if ((operand->flags & S390_OPERAND_PCREL)
&& (operand->bits == 32))
reloc = BFD_RELOC_390_GOTPLTENT;
}
else if (suffix == ELF_SUFFIX_TLS_GOTIE)
{
if ((operand->flags & S390_OPERAND_DISP)
&& (operand->bits == 12))
reloc = BFD_RELOC_390_TLS_GOTIE12;
else if ((operand->flags & S390_OPERAND_DISP)
&& (operand->bits == 20))
reloc = BFD_RELOC_390_TLS_GOTIE20;
}
else if (suffix == ELF_SUFFIX_TLS_IE)
{
if ((operand->flags & S390_OPERAND_PCREL)
&& (operand->bits == 32))
reloc = BFD_RELOC_390_TLS_IEENT;
}
if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
as_bad (_("invalid operand suffix"));
if (fc >= MAX_INSN_FIXUPS)
as_fatal (_("too many fixups"));
fixups[fc].exp = ex;
fixups[fc].opindex = *opindex_ptr;
fixups[fc].reloc = reloc;
++fc;
}
if (operand->flags & S390_OPERAND_DISP)
{
if (*str != '(')
{
operand = s390_operands + *(++opindex_ptr);
if (!(operand->flags & (S390_OPERAND_INDEX|S390_OPERAND_BASE)))
as_bad (_("syntax error; missing '(' after displacement"));
while (!(operand->flags & S390_OPERAND_BASE))
operand = s390_operands + *(++opindex_ptr);
if (opindex_ptr[1] != '\0')
{
if (*str++ != ',')
as_bad (_("syntax error; expected ,"));
}
}
else
{
str++;
for (f = str; *f != '\0'; f++)
if (*f == ',' || *f == ')')
break;
if (*f == ',' && f == str)
{
skip_optional = 1;
str++;
}
else
skip_optional = (*f != ',');
}
}
else if (operand->flags & S390_OPERAND_BASE)
{
if (*str++ != ')')
as_bad (_("syntax error; missing ')' after base register"));
skip_optional = 0;
if (opindex_ptr[1] != '\0')
{
if (*str++ != ',')
as_bad (_("syntax error; expected ,"));
}
}
else
{
if (*str == ')')
{
operand = s390_operands + *(++opindex_ptr);
if (!(operand->flags & S390_OPERAND_BASE))
as_bad (_("syntax error; ')' not allowed here"));
str++;
}
if (opindex_ptr[1] != '\0')
{
if (*str++ != ',')
as_bad (_("syntax error; expected ,"));
}
}
}
while (ISSPACE (*str))
++str;
reloc = s390_tls_suffix (&str, &ex);
if (reloc != BFD_RELOC_UNUSED)
{
if (fc >= MAX_INSN_FIXUPS)
as_fatal (_("too many fixups"));
fixups[fc].exp = ex;
fixups[fc].opindex = -1;
fixups[fc].reloc = reloc;
++fc;
}
if (*str != '\0')
{
char *linefeed;
if ((linefeed = strchr (str, '\n')) != NULL)
*linefeed = '\0';
as_bad (_("junk at end of line: `%s'"), str);
if (linefeed != NULL)
*linefeed = '\n';
}
f = frag_more (opcode->oplen);
memcpy (f, insn, opcode->oplen);
dwarf2_emit_insn (opcode->oplen);
for (i = 0; i < fc; i++)
{
if (fixups[i].opindex < 0)
{
fix_new_exp (frag_now, f - frag_now->fr_literal, opcode->oplen,
&fixups[i].exp, 0, fixups[i].reloc);
continue;
}
operand = s390_operands + fixups[i].opindex;
if (fixups[i].reloc != BFD_RELOC_UNUSED)
{
reloc_howto_type *reloc_howto;
fixS *fixP;
int size;
reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
if (!reloc_howto)
abort ();
size = bfd_get_reloc_size (reloc_howto);
if (size < 1 || size > 4)
abort ();
fixP = fix_new_exp (frag_now,
f - frag_now->fr_literal + (operand->shift/8),
size, &fixups[i].exp, reloc_howto->pc_relative,
fixups[i].reloc);
if ( fixups[i].reloc == BFD_RELOC_390_GOT12
|| fixups[i].reloc == BFD_RELOC_390_GOT20
|| fixups[i].reloc == BFD_RELOC_390_GOT16)
fixP->fx_no_overflow = 1;
}
else
fix_new_exp (frag_now, f - frag_now->fr_literal, 4, &fixups[i].exp,
(operand->flags & S390_OPERAND_PCREL) != 0,
((bfd_reloc_code_real_type)
(fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
}
return str;
}
void
md_assemble (str)
char *str;
{
const struct s390_opcode *opcode;
unsigned char insn[6];
char *s;
for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
;
if (*s != '\0')
*s++ = '\0';
opcode = (struct s390_opcode *) hash_find (s390_opcode_hash, str);
if (opcode == (const struct s390_opcode *) NULL)
{
as_bad (_("Unrecognized opcode: `%s'"), str);
return;
}
else if (!(opcode->modes & current_mode_mask))
{
as_bad ("Opcode %s not available in this mode", str);
return;
}
memcpy (insn, opcode->opcode, sizeof (insn));
md_gather_operands (s, insn, opcode);
}
#ifndef WORKING_DOT_WORD
void
md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
char *ptr;
addressT from_addr, to_addr;
fragS *frag;
symbolS *to_symbol;
{
abort ();
}
void
md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
char *ptr;
addressT from_addr, to_addr;
fragS *frag;
symbolS *to_symbol;
{
abort ();
}
#endif
void
s390_bss (ignore)
int ignore ATTRIBUTE_UNUSED;
{
subseg_set (bss_section, 0);
demand_empty_rest_of_line ();
}
void
s390_insn (ignore)
int ignore ATTRIBUTE_UNUSED;
{
expressionS exp;
const struct s390_opcode *opformat;
unsigned char insn[6];
char *s;
s = input_line_pointer;
while (*s != '\0' && *s != ',' && ! ISSPACE (*s))
s++;
if (*s != ',')
as_bad (_("Invalid .insn format\n"));
*s++ = '\0';
opformat = (struct s390_opcode *)
hash_find (s390_opformat_hash, input_line_pointer);
if (opformat == (const struct s390_opcode *) NULL)
{
as_bad (_("Unrecognized opcode format: `%s'"), input_line_pointer);
return;
}
input_line_pointer = s;
expression (&exp);
if (exp.X_op == O_constant)
{
if ( ( opformat->oplen == 6
&& (addressT) exp.X_add_number < (1ULL << 48))
|| ( opformat->oplen == 4
&& (addressT) exp.X_add_number < (1ULL << 32))
|| ( opformat->oplen == 2
&& (addressT) exp.X_add_number < (1ULL << 16)))
md_number_to_chars ((char *) insn, exp.X_add_number, opformat->oplen);
else
as_bad (_("Invalid .insn format\n"));
}
else if (exp.X_op == O_big)
{
if (exp.X_add_number > 0
&& opformat->oplen == 6
&& generic_bignum[3] == 0)
{
md_number_to_chars ((char *) insn, generic_bignum[2], 2);
md_number_to_chars ((char *) &insn[2], generic_bignum[1], 2);
md_number_to_chars ((char *) &insn[4], generic_bignum[0], 2);
}
else
as_bad (_("Invalid .insn format\n"));
}
else
as_bad (_("second operand of .insn not a constant\n"));
if (strcmp (opformat->name, "e") != 0 && *input_line_pointer++ != ',')
as_bad (_("missing comma after insn constant\n"));
if ((s = strchr (input_line_pointer, '\n')) != NULL)
*s = '\0';
input_line_pointer = md_gather_operands (input_line_pointer, insn,
opformat);
if (s != NULL)
*s = '\n';
demand_empty_rest_of_line ();
}
static void
s390_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
s390_literals (ignore)
int ignore ATTRIBUTE_UNUSED;
{
struct s390_lpe *lpe;
if (lp_sym == NULL || lpe_count == 0)
return;
S_SET_SEGMENT (lp_sym, now_seg);
S_SET_VALUE (lp_sym, (valueT) frag_now_fix ());
lp_sym->sy_frag = frag_now;
while (lpe_list)
{
lpe = lpe_list;
lpe_list = lpe_list->next;
S_SET_SEGMENT (lpe->sym, now_seg);
S_SET_VALUE (lpe->sym, (valueT) frag_now_fix ());
lpe->sym->sy_frag = frag_now;
if (lpe->reloc != BFD_RELOC_UNUSED)
{
reloc_howto_type *reloc_howto =
bfd_reloc_type_lookup (stdoutput, lpe->reloc);
int size = bfd_get_reloc_size (reloc_howto);
char *where;
if (size > lpe->nbytes)
as_bad (_("%s relocations do not fit in %d bytes"),
reloc_howto->name, lpe->nbytes);
where = frag_more (lpe->nbytes);
md_number_to_chars (where, 0, size);
fix_new_exp (frag_now, where - frag_now->fr_literal,
size, &lpe->ex, reloc_howto->pc_relative, lpe->reloc);
}
else
{
if (lpe->ex.X_op == O_big)
{
if (lpe->ex.X_add_number <= 0)
generic_floating_point_number = lpe->floatnum;
else
memcpy (generic_bignum, lpe->bignum,
lpe->ex.X_add_number * sizeof (LITTLENUM_TYPE));
}
emit_expr (&lpe->ex, lpe->nbytes);
}
lpe->next = lpe_free_list;
lpe_free_list = lpe;
}
lpe_list_tail = NULL;
lp_sym = NULL;
lp_count++;
lpe_count = 0;
}
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':
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;
for (i = 0; i < prec; i++)
{
md_number_to_chars (litp, (valueT) words[i], 2);
litp += 2;
}
return NULL;
}
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;
{
if (*name == '_' && *(name + 1) == 'G'
&& strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
{
if (!GOT_symbol)
{
if (symbol_find (name))
as_bad (_("GOT already in symbol table"));
GOT_symbol = symbol_new (name, undefined_section,
(valueT) 0, &zero_address_frag);
}
return GOT_symbol;
}
return 0;
}
long
md_pcrel_from_section (fixp, sec)
fixS *fixp;
segT sec ATTRIBUTE_UNUSED;
{
return fixp->fx_frag->fr_address + fixp->fx_where;
}
int
tc_s390_fix_adjustable (fixP)
fixS *fixP;
{
if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0)
return 0;
if ( fixP->fx_r_type == BFD_RELOC_16_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_32_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_390_GOTOFF64
|| fixP->fx_r_type == BFD_RELOC_390_PLTOFF16
|| fixP->fx_r_type == BFD_RELOC_390_PLTOFF32
|| fixP->fx_r_type == BFD_RELOC_390_PLTOFF64
|| fixP->fx_r_type == BFD_RELOC_390_PLT16DBL
|| fixP->fx_r_type == BFD_RELOC_390_PLT32
|| fixP->fx_r_type == BFD_RELOC_390_PLT32DBL
|| fixP->fx_r_type == BFD_RELOC_390_PLT64
|| fixP->fx_r_type == BFD_RELOC_390_GOT12
|| fixP->fx_r_type == BFD_RELOC_390_GOT20
|| fixP->fx_r_type == BFD_RELOC_390_GOT16
|| fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
|| fixP->fx_r_type == BFD_RELOC_390_GOT64
|| fixP->fx_r_type == BFD_RELOC_390_GOTENT
|| fixP->fx_r_type == BFD_RELOC_390_GOTPLT12
|| fixP->fx_r_type == BFD_RELOC_390_GOTPLT16
|| fixP->fx_r_type == BFD_RELOC_390_GOTPLT20
|| fixP->fx_r_type == BFD_RELOC_390_GOTPLT32
|| fixP->fx_r_type == BFD_RELOC_390_GOTPLT64
|| fixP->fx_r_type == BFD_RELOC_390_GOTPLTENT
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LOAD
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GDCALL
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LDCALL
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GD32
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GD64
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE12
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE20
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE32
|| fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE64
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LDM32
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LDM64
|| fixP->fx_r_type == BFD_RELOC_390_TLS_IE32
|| fixP->fx_r_type == BFD_RELOC_390_TLS_IE64
|| fixP->fx_r_type == BFD_RELOC_390_TLS_IEENT
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LE32
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LE64
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LDO32
|| fixP->fx_r_type == BFD_RELOC_390_TLS_LDO64
|| fixP->fx_r_type == BFD_RELOC_390_TLS_DTPMOD
|| fixP->fx_r_type == BFD_RELOC_390_TLS_DTPOFF
|| fixP->fx_r_type == BFD_RELOC_390_TLS_TPOFF
|| fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
return 0;
return 1;
}
int
tc_s390_force_relocation (fixp)
struct fix *fixp;
{
switch (fixp->fx_r_type)
{
case BFD_RELOC_390_GOT12:
case BFD_RELOC_390_GOT20:
case BFD_RELOC_32_GOT_PCREL:
case BFD_RELOC_32_GOTOFF:
case BFD_RELOC_390_GOTOFF64:
case BFD_RELOC_390_PLTOFF16:
case BFD_RELOC_390_PLTOFF32:
case BFD_RELOC_390_PLTOFF64:
case BFD_RELOC_390_GOTPC:
case BFD_RELOC_390_GOT16:
case BFD_RELOC_390_GOTPCDBL:
case BFD_RELOC_390_GOT64:
case BFD_RELOC_390_GOTENT:
case BFD_RELOC_390_PLT32:
case BFD_RELOC_390_PLT16DBL:
case BFD_RELOC_390_PLT32DBL:
case BFD_RELOC_390_PLT64:
case BFD_RELOC_390_GOTPLT12:
case BFD_RELOC_390_GOTPLT16:
case BFD_RELOC_390_GOTPLT20:
case BFD_RELOC_390_GOTPLT32:
case BFD_RELOC_390_GOTPLT64:
case BFD_RELOC_390_GOTPLTENT:
return 1;
default:
break;;
}
return generic_force_reloc (fixp);
}
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT *valP;
segT seg ATTRIBUTE_UNUSED;
{
char *where;
valueT value = *valP;
where = fixP->fx_frag->fr_literal + fixP->fx_where;
if (fixP->fx_subsy != NULL)
as_bad_where (fixP->fx_file, fixP->fx_line,
"cannot emit relocation %s against subsy symbol %s",
bfd_get_reloc_code_name (fixP->fx_r_type),
S_GET_NAME (fixP->fx_subsy));
if (fixP->fx_addsy != NULL)
{
if (fixP->fx_pcrel)
value += fixP->fx_frag->fr_address + fixP->fx_where;
}
else
fixP->fx_done = 1;
if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
{
const struct s390_operand *operand;
int opindex;
opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
operand = &s390_operands[opindex];
if (fixP->fx_done)
{
s390_insert_operand ((unsigned char *) where, operand,
(offsetT) value, fixP->fx_file, fixP->fx_line);
return;
}
fixP->fx_offset = value;
if (operand->bits == 12 && operand->shift == 20)
{
fixP->fx_size = 2;
fixP->fx_where += 2;
fixP->fx_r_type = BFD_RELOC_390_12;
}
else if (operand->bits == 12 && operand->shift == 36)
{
fixP->fx_size = 2;
fixP->fx_where += 4;
fixP->fx_r_type = BFD_RELOC_390_12;
}
else if (operand->bits == 20 && operand->shift == 20)
{
fixP->fx_size = 2;
fixP->fx_where += 2;
fixP->fx_r_type = BFD_RELOC_390_20;
}
else if (operand->bits == 8 && operand->shift == 8)
{
fixP->fx_size = 1;
fixP->fx_where += 1;
fixP->fx_r_type = BFD_RELOC_8;
}
else if (operand->bits == 16 && operand->shift == 16)
{
fixP->fx_size = 2;
fixP->fx_where += 2;
if (operand->flags & S390_OPERAND_PCREL)
{
fixP->fx_r_type = BFD_RELOC_390_PC16DBL;
fixP->fx_offset += 2;
}
else
fixP->fx_r_type = BFD_RELOC_16;
}
else if (operand->bits == 32 && operand->shift == 16
&& (operand->flags & S390_OPERAND_PCREL))
{
fixP->fx_size = 4;
fixP->fx_where += 2;
fixP->fx_offset += 2;
fixP->fx_r_type = BFD_RELOC_390_PC32DBL;
}
else
{
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
{
switch (fixP->fx_r_type)
{
case BFD_RELOC_8:
if (fixP->fx_pcrel)
abort ();
if (fixP->fx_done)
md_number_to_chars (where, value, 1);
break;
case BFD_RELOC_390_12:
case BFD_RELOC_390_GOT12:
case BFD_RELOC_390_GOTPLT12:
if (fixP->fx_done)
{
unsigned short mop;
mop = bfd_getb16 ((unsigned char *) where);
mop |= (unsigned short) (value & 0xfff);
bfd_putb16 ((bfd_vma) mop, (unsigned char *) where);
}
break;
case BFD_RELOC_390_20:
case BFD_RELOC_390_GOT20:
case BFD_RELOC_390_GOTPLT20:
if (fixP->fx_done)
{
unsigned int mop;
mop = bfd_getb32 ((unsigned char *) where);
mop |= (unsigned int) ((value & 0xfff) << 8 |
(value & 0xff000) >> 12);
bfd_putb32 ((bfd_vma) mop, (unsigned char *) where);
}
break;
case BFD_RELOC_16:
case BFD_RELOC_GPREL16:
case BFD_RELOC_16_GOT_PCREL:
case BFD_RELOC_16_GOTOFF:
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)
: ""));
if (fixP->fx_done)
md_number_to_chars (where, value, 2);
break;
case BFD_RELOC_390_GOT16:
case BFD_RELOC_390_PLTOFF16:
case BFD_RELOC_390_GOTPLT16:
if (fixP->fx_done)
md_number_to_chars (where, value, 2);
break;
case BFD_RELOC_390_PC16DBL:
case BFD_RELOC_390_PLT16DBL:
value += 2;
if (fixP->fx_done)
md_number_to_chars (where, (offsetT) value >> 1, 2);
break;
case BFD_RELOC_32:
if (fixP->fx_pcrel)
fixP->fx_r_type = BFD_RELOC_32_PCREL;
else
fixP->fx_r_type = BFD_RELOC_32;
if (fixP->fx_done)
md_number_to_chars (where, value, 4);
break;
case BFD_RELOC_32_PCREL:
case BFD_RELOC_32_BASEREL:
fixP->fx_r_type = BFD_RELOC_32_PCREL;
if (fixP->fx_done)
md_number_to_chars (where, value, 4);
break;
case BFD_RELOC_32_GOT_PCREL:
case BFD_RELOC_390_PLTOFF32:
case BFD_RELOC_390_PLT32:
case BFD_RELOC_390_GOTPLT32:
if (fixP->fx_done)
md_number_to_chars (where, value, 4);
break;
case BFD_RELOC_390_PC32DBL:
case BFD_RELOC_390_PLT32DBL:
case BFD_RELOC_390_GOTPCDBL:
case BFD_RELOC_390_GOTENT:
case BFD_RELOC_390_GOTPLTENT:
value += 2;
if (fixP->fx_done)
md_number_to_chars (where, (offsetT) value >> 1, 4);
break;
case BFD_RELOC_32_GOTOFF:
if (fixP->fx_done)
md_number_to_chars (where, value, sizeof (int));
break;
case BFD_RELOC_390_GOTOFF64:
if (fixP->fx_done)
md_number_to_chars (where, value, 8);
break;
case BFD_RELOC_390_GOT64:
case BFD_RELOC_390_PLTOFF64:
case BFD_RELOC_390_PLT64:
case BFD_RELOC_390_GOTPLT64:
if (fixP->fx_done)
md_number_to_chars (where, value, 8);
break;
case BFD_RELOC_64:
if (fixP->fx_pcrel)
fixP->fx_r_type = BFD_RELOC_64_PCREL;
else
fixP->fx_r_type = BFD_RELOC_64;
if (fixP->fx_done)
md_number_to_chars (where, value, 8);
break;
case BFD_RELOC_64_PCREL:
fixP->fx_r_type = BFD_RELOC_64_PCREL;
if (fixP->fx_done)
md_number_to_chars (where, value, 8);
break;
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
fixP->fx_done = 0;
return;
case BFD_RELOC_390_TLS_LOAD:
case BFD_RELOC_390_TLS_GDCALL:
case BFD_RELOC_390_TLS_LDCALL:
case BFD_RELOC_390_TLS_GD32:
case BFD_RELOC_390_TLS_GD64:
case BFD_RELOC_390_TLS_GOTIE12:
case BFD_RELOC_390_TLS_GOTIE20:
case BFD_RELOC_390_TLS_GOTIE32:
case BFD_RELOC_390_TLS_GOTIE64:
case BFD_RELOC_390_TLS_LDM32:
case BFD_RELOC_390_TLS_LDM64:
case BFD_RELOC_390_TLS_IE32:
case BFD_RELOC_390_TLS_IE64:
case BFD_RELOC_390_TLS_LE32:
case BFD_RELOC_390_TLS_LE64:
case BFD_RELOC_390_TLS_LDO32:
case BFD_RELOC_390_TLS_LDO64:
case BFD_RELOC_390_TLS_DTPMOD:
case BFD_RELOC_390_TLS_DTPOFF:
case BFD_RELOC_390_TLS_TPOFF:
S_SET_THREAD_LOCAL (fixP->fx_addsy);
break;
case BFD_RELOC_390_TLS_IEENT:
S_SET_THREAD_LOCAL (fixP->fx_addsy);
value += 2;
break;
default:
{
const char *reloc_name = bfd_get_reloc_code_name (fixP->fx_r_type);
if (reloc_name != NULL)
fprintf (stderr, "Gas failure, reloc type %s\n", reloc_name);
else
fprintf (stderr, "Gas failure, reloc type #%i\n", fixP->fx_r_type);
fflush (stderr);
abort ();
}
}
fixP->fx_offset = value;
}
}
arelent *
tc_gen_reloc (seg, fixp)
asection *seg ATTRIBUTE_UNUSED;
fixS *fixp;
{
bfd_reloc_code_real_type code;
arelent *reloc;
code = fixp->fx_r_type;
if (GOT_symbol && fixp->fx_addsy == GOT_symbol)
{
if ( (s390_arch_size == 32 && code == BFD_RELOC_32_PCREL)
|| (s390_arch_size == 64 && code == BFD_RELOC_64_PCREL))
code = BFD_RELOC_390_GOTPC;
if (code == BFD_RELOC_390_PC32DBL)
code = BFD_RELOC_390_GOTPCDBL;
}
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, code);
if (reloc->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("cannot represent relocation type %s"),
bfd_get_reloc_code_name (code));
reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
assert (reloc->howto != NULL);
}
reloc->addend = fixp->fx_offset;
return reloc;
}
void
s390_cfi_frame_initial_instructions ()
{
cfi_add_CFA_def_cfa (15, s390_arch_size == 64 ? 160 : 96);
}
int
tc_s390_regname_to_dw2regnum (const char *regname)
{
int regnum = -1;
if (regname[0] != 'c' && regname[0] != 'a')
{
regnum = reg_name_search (pre_defined_registers, REG_NAME_CNT, regname);
if (regname[0] == 'f' && regnum != -1)
regnum += 16;
}
else if (strcmp (regname, "ap") == 0)
regnum = 32;
else if (strcmp (regname, "cc") == 0)
regnum = 33;
return regnum;
}