X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-s390.c;h=098debc8d2d88baab1715c3138b958f78dfec18c;hb=97f50151221de0a023a8317559b1992a90f9cb8f;hp=97edcc3b69b87d69bd08484b8228a54f98f13539;hpb=2ebb4b8890392116df80469e328ad7988e04b193;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-s390.c b/gas/config/tc-s390.c index 97edcc3b69..098debc8d2 100644 --- a/gas/config/tc-s390.c +++ b/gas/config/tc-s390.c @@ -1,12 +1,12 @@ /* tc-s390.c -- Assemble for the S390 - Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2000-2016 Free Software Foundation, Inc. Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). 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) + the Free Software Foundation; either version 3, or (at your option) any later version. GAS is distributed in the hope that it will be useful, @@ -16,15 +16,15 @@ 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. */ + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ -#include #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" @@ -33,12 +33,20 @@ #ifndef DEFAULT_ARCH #define DEFAULT_ARCH "s390" #endif -static char *default_arch = DEFAULT_ARCH; +static const char *default_arch = DEFAULT_ARCH; /* Either 32 or 64, selects file format. */ static int s390_arch_size = 0; +/* If no -march option was given default to the highest available CPU. + Since with S/390 a newer CPU always supports everything from its + predecessors this will accept every valid asm input. */ +static unsigned int current_cpu = S390_OPCODE_MAXCPU - 1; static unsigned int current_mode_mask = 0; -static unsigned int current_cpu = -1U; +static unsigned int current_flags = 0; + +/* Set to TRUE if the highgprs flag in the ELF header needs to be set + for the output file. */ +static bfd_boolean set_highgprs_p = FALSE; /* Whether to use user friendly register names. Default is TRUE. */ #ifndef TARGET_REG_NAMES_P @@ -70,176 +78,67 @@ const char EXP_CHARS[] = "eE"; as in 0d1.0. */ const char FLT_CHARS[] = "dD"; +/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */ +int s390_cie_data_alignment; + /* The target specific pseudo-ops which we support. */ /* Define the prototypes for the pseudo-ops */ -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)); +static void s390_byte (int); +static void s390_elf_cons (int); +static void s390_bss (int); +static void s390_insn (int); +static void s390_literals (int); +static void s390_machine (int); +static void s390_machinemode (int); const pseudo_typeS md_pseudo_table[] = { - { "align", s_align_bytes, 0 }, + { "align", s_align_bytes, 0 }, /* Pseudo-ops which must be defined. */ - { "bss", s390_bss, 0 }, - { "insn", s390_insn, 0 }, + { "bss", s390_bss, 0 }, + { "insn", s390_insn, 0 }, /* Pseudo-ops which must be overridden. */ - { "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 } + { "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, 8 + 1 }, + { "machine", s390_machine, 0 }, + { "machinemode", s390_machinemode, 0 }, + { NULL, NULL, 0 } }; - -/* Structure to hold information about predefined registers. */ -struct pd_reg - { - char *name; - int value; - }; - -/* List of registers that are pre-defined: - - Each access register has a predefined name of the form: - a which has the value . - - Each control register has a predefined name of the form: - c which has the value . - - Each general register has a predefined name of the form: - r which has the value . - - Each floating point register a has predefined name of the form: - f which has the value . - - There are individual registers as well: - sp has the value 15 - lit has the value 12 - - The table is sorted. Suitable for searching by a binary search. */ - -static const struct pd_reg pre_defined_registers[] = -{ - { "a0", 0 }, /* Access registers */ - { "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 }, /* Control registers */ - { "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 }, /* Floating point registers */ - { "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 }, /* Pointer to literal pool */ - - { "r0", 0 }, /* General purpose registers */ - { "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 }, /* Stack pointer */ - -}; - -#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 *)); - /* Given NAME, find the register number associated with that name, return the integer value associated with the given name or -1 on failure. */ static int -reg_name_search (regs, regcount, name) - const struct pd_reg *regs; - int regcount; - const char *name; +reg_name_search (const char *name) { - int middle, low, high; - int cmp; + int val = -1; - low = 0; - high = regcount - 1; + if (strcasecmp (name, "lit") == 0) + return 13; - do + if (strcasecmp (name, "sp") == 0) + return 15; + + if (name[0] != 'a' && name[0] != 'c' && name[0] != 'f' + && name[0] != 'r' && name[0] != 'v') + return -1; + + if (ISDIGIT (name[1])) { - 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; + val = name[1] - '0'; + if (ISDIGIT (name[2])) + val = val * 10 + name[2] - '0'; } - while (low <= high); - return -1; + if ((name[0] != 'v' && val > 15) || val > 31) + val = -1; + + return val; } @@ -256,8 +155,7 @@ reg_name_search (regs, regcount, name) */ static bfd_boolean -register_name (expressionP) - expressionS *expressionP; +register_name (expressionS *expressionP) { int reg_number; char *name; @@ -271,11 +169,11 @@ register_name (expressionP) else return FALSE; - c = get_symbol_end (); - reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name); + c = get_symbol_name (&name); + reg_number = reg_name_search (name); /* Put back the delimiting char. */ - *input_line_pointer = c; + (void) restore_line_pointer (c); /* Look to see if it's in the register table. */ if (reg_number >= 0) @@ -300,7 +198,7 @@ register_name (expressionP) static struct hash_control *s390_opformat_hash; /* Opcode hash table. */ -static struct hash_control *s390_opcode_hash; +static struct hash_control *s390_opcode_hash = NULL; /* Flags to set in the elf header */ static flagword s390_flags = 0; @@ -308,8 +206,8 @@ static flagword s390_flags = 0; symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ #ifndef WORKING_DOT_WORD -const int md_short_jump_size = 4; -const int md_long_jump_size = 4; +int md_short_jump_size = 4; +int md_long_jump_size = 4; #endif const char *md_shortopts = "A:m:kVQ:"; @@ -321,33 +219,34 @@ size_t md_longopts_size = sizeof (md_longopts); /* Initialize the default opcode arch and word size from the default architecture name if not specified by an option. */ static void -init_default_arch () +init_default_arch (void) { if (strcmp (default_arch, "s390") == 0) { if (s390_arch_size == 0) s390_arch_size = 32; - if (current_mode_mask == 0) - current_mode_mask = 1 << S390_OPCODE_ESA; - if (current_cpu == -1U) - current_cpu = S390_OPCODE_G5; } else if (strcmp (default_arch, "s390x") == 0) { if (s390_arch_size == 0) s390_arch_size = 64; - if (current_mode_mask == 0) - current_mode_mask = 1 << S390_OPCODE_ZARCH; - if (current_cpu == -1U) - current_cpu = S390_OPCODE_Z900; } else - as_fatal ("Invalid default architecture, broken assembler."); + as_fatal (_("Invalid default architecture, broken assembler.")); + + if (current_mode_mask == 0) + { + /* Default to z/Architecture mode if the CPU supports it. */ + if (current_cpu < S390_OPCODE_Z900) + current_mode_mask = 1 << S390_OPCODE_ESA; + else + current_mode_mask = 1 << S390_OPCODE_ZARCH; + } } /* Called by TARGET_FORMAT. */ const char * -s390_target_format () +s390_target_format (void) { /* We don't get a chance to initialize anything before we're called, so handle that now. */ @@ -356,10 +255,120 @@ s390_target_format () return s390_arch_size == 64 ? "elf64-s390" : "elf32-s390"; } +/* Map a cpu string ARG as given with -march= or .machine to the respective + enum s390_opcode_cpu_val value. If ALLOW_EXTENSIONS is TRUE, the cpu name + can be followed by a list of cpu facility flags each beginning with the + character '+'. The active cpu flags are returned through *RET_FLAGS. + In case of an error, S390_OPCODE_MAXCPU is returned. */ + +static unsigned int +s390_parse_cpu (const char * arg, + unsigned int * ret_flags, + bfd_boolean allow_extensions) +{ + static struct + { + const char * name; + unsigned int len; + unsigned int flags; + } cpu_table[S390_OPCODE_MAXCPU] = + { + { STRING_COMMA_LEN ("g5"), 0 }, + { STRING_COMMA_LEN ("g6"), 0 }, + { STRING_COMMA_LEN ("z900"), 0 }, + { STRING_COMMA_LEN ("z990"), 0 }, + { STRING_COMMA_LEN ("z9-109"), 0 }, + { STRING_COMMA_LEN ("z9-ec"), 0 }, + { STRING_COMMA_LEN ("z10"), 0 }, + { STRING_COMMA_LEN ("z196"), 0 }, + { STRING_COMMA_LEN ("zEC12"), S390_INSTR_FLAG_HTM }, + { STRING_COMMA_LEN ("z13"), S390_INSTR_FLAG_HTM | S390_INSTR_FLAG_VX } + }; + static struct + { + const char * name; + unsigned int mask; + bfd_boolean on; + } cpu_flags[] = + { + { "htm", S390_INSTR_FLAG_HTM, TRUE }, + { "nohtm", S390_INSTR_FLAG_HTM, FALSE }, + { "vx", S390_INSTR_FLAG_VX, TRUE }, + { "novx", S390_INSTR_FLAG_VX, FALSE } + }; + unsigned int icpu; + char *ilp_bak; + + icpu = S390_OPCODE_MAXCPU; + if (strncmp (arg, "all", 3) == 0 && (arg[3] == 0 || arg[3] == '+')) + { + icpu = S390_OPCODE_MAXCPU - 1; + arg += 3; + } + else + { + for (icpu = 0; icpu < S390_OPCODE_MAXCPU; icpu++) + { + unsigned int l; + + l = cpu_table[icpu].len; + if (strncmp (arg, cpu_table[icpu].name, l) == 0 + && (arg[l] == 0 || arg[l] == '+')) + { + arg += l; + break; + } + } + } + + ilp_bak = input_line_pointer; + if (icpu != S390_OPCODE_MAXCPU) + { + input_line_pointer = (char *) arg; + *ret_flags = (cpu_table[icpu].flags & S390_INSTR_FLAG_FACILITY_MASK); + + while (*input_line_pointer == '+' && allow_extensions) + { + unsigned int iflag; + char *sym; + char c; + + input_line_pointer++; + c = get_symbol_name (&sym); + for (iflag = 0; iflag < ARRAY_SIZE (cpu_flags); iflag++) + { + if (strcmp (sym, cpu_flags[iflag].name) == 0) + { + if (cpu_flags[iflag].on) + *ret_flags |= cpu_flags[iflag].mask; + else + *ret_flags &= ~cpu_flags[iflag].mask; + break; + } + } + if (iflag == ARRAY_SIZE (cpu_flags)) + as_bad (_("no such machine extension `%s'"), sym - 1); + *input_line_pointer = c; + if (iflag == ARRAY_SIZE (cpu_flags)) + break; + } + } + + SKIP_WHITESPACE (); + + if (*input_line_pointer != 0 && *input_line_pointer != '\n') + { + as_bad (_("junk at end of machine string, first unrecognized character" + " is `%c'"), *input_line_pointer); + icpu = S390_OPCODE_MAXCPU; + } + input_line_pointer = ilp_bak; + + return icpu; +} + int -md_parse_option (c, arg) - int c; - char *arg; +md_parse_option (int c, const char *arg) { switch (c) { @@ -386,17 +395,16 @@ md_parse_option (c, arg) current_mode_mask = 1 << S390_OPCODE_ESA; else if (arg != NULL && strcmp (arg, "zarch") == 0) - current_mode_mask = 1 << S390_OPCODE_ZARCH; + { + if (s390_arch_size == 32) + set_highgprs_p = TRUE; + 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 + current_cpu = s390_parse_cpu (arg + 5, ¤t_flags, FALSE); + if (current_cpu == S390_OPCODE_MAXCPU) { as_bad (_("invalid switch -m%s"), arg); return 0; @@ -411,13 +419,13 @@ md_parse_option (c, arg) break; case 'A': - /* Option -A is deprecated. Still available for compatability. */ + /* Option -A is deprecated. Still available for compatibility. */ 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); + as_bad (_("invalid architecture -A%s"), arg); break; /* -V: SVR4 argument to print version ID. */ @@ -438,8 +446,7 @@ md_parse_option (c, arg) } void -md_show_usage (stream) - FILE *stream; +md_show_usage (FILE *stream) { fprintf (stream, _("\ S390 options:\n\ @@ -453,21 +460,88 @@ md_show_usage (stream) -Qy, -Qn ignored\n")); } +/* Generate the hash table mapping mnemonics to struct s390_opcode. + This table is built at startup and whenever the CPU level is + changed using .machine. */ + +static void +s390_setup_opcodes (void) +{ + const struct s390_opcode *op; + const struct s390_opcode *op_end; + bfd_boolean dup_insn = FALSE; + const char *retval; + + if (s390_opcode_hash != NULL) + hash_die (s390_opcode_hash); + + /* Insert the opcodes into a hash table. */ + s390_opcode_hash = hash_new (); + + op_end = s390_opcodes + s390_num_opcodes; + for (op = s390_opcodes; op < op_end; op++) + { + int use_opcode; + + while (op < op_end - 1 && strcmp(op->name, op[1].name) == 0) + { + if (op->min_cpu <= current_cpu && (op->modes & current_mode_mask)) + break; + op++; + } + + if ((op->modes & current_mode_mask) == 0) + use_opcode = 0; + else if ((op->flags & S390_INSTR_FLAG_FACILITY_MASK) == 0) + { + /* Opcodes that do not belong to a specific facility are enabled if + present in the selected cpu. */ + use_opcode = (op->min_cpu <= current_cpu); + } + else + { + unsigned int f; + + /* Opcodes of a specific facility are enabled if the facility is + enabled. Note: only some facilities are represented as flags. */ + f = (op->flags & S390_INSTR_FLAG_FACILITY_MASK); + use_opcode = ((f & current_flags) == f); + } + if (use_opcode) + { + retval = hash_insert (s390_opcode_hash, op->name, (void *) 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 (); +} + /* This function is called when the assembler starts up. It is called after the options have been parsed and the output file has been opened. */ void -md_begin () +md_begin (void) { - register const struct s390_opcode *op; + const struct s390_opcode *op; const struct s390_opcode *op_end; - bfd_boolean dup_insn = FALSE; const char *retval; /* Give a warning if the combination -m64-bit and -Aesa is used. */ if (s390_arch_size == 64 && current_cpu < S390_OPCODE_Z900) - as_warn ("The 64 bit file format is used without esame instructions."); + as_warn (_("The 64 bit file format is used without esame instructions.")); + + s390_cie_data_alignment = -s390_arch_size / 8; /* Set the ELF flags if desired. */ if (s390_flags) @@ -479,41 +553,22 @@ md_begin () 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); + retval = hash_insert (s390_opformat_hash, op->name, (void *) op); if (retval != (const char *) NULL) - { - as_bad (_("Internal assembler error for instruction format %s"), - op->name); - dup_insn = TRUE; - } + as_bad (_("Internal assembler error for instruction format %s"), + op->name); } - /* Insert the opcodes into a hash table. */ - s390_opcode_hash = hash_new (); - - op_end = s390_opcodes + s390_num_opcodes; - for (op = s390_opcodes; op < op_end; op++) - { - 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; - } - } - - if (dup_insn) - abort (); + s390_setup_opcodes (); record_alignment (text_section, 2); record_alignment (data_section, 2); record_alignment (bss_section, 2); - } /* Called after all assembly has been done. */ void -s390_md_end () +s390_md_end (void) { if (s390_arch_size == 64) bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_64); @@ -521,28 +576,14 @@ s390_md_end () bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_31); } -void -s390_align_code (fragP, count) - fragS *fragP; - int count; -{ - /* We use nop pattern 0x0707. */ - if (count > 0) - { - memset (fragP->fr_literal + fragP->fr_fix, 0x07, count); - fragP->fr_var = count; - } -} - /* Insert an operand value into an instruction. */ 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; +s390_insert_operand (unsigned char *insn, + const struct s390_operand *operand, + offsetT val, + const char *file, + unsigned int line) { addressT uval; int offset; @@ -560,7 +601,7 @@ s390_insert_operand (insn, operand, val, file, line) if (val < min || val > max) { const char *err = - "operand out of range (%s not between %ld and %ld)"; + _("operand out of range (%s not between %ld and %ld)"); char buf[100]; if (operand->flags & S390_OPERAND_PCREL) @@ -578,6 +619,9 @@ s390_insert_operand (insn, operand, val, file, line) } /* val is ok, now restrict it to operand->bits bits. */ uval = (addressT) val & ((((addressT) 1 << (operand->bits-1)) << 1) - 1); + /* val is restrict, now check for special case. */ + if (operand->bits == 20 && operand->shift == 20) + uval = (uval >> 12) | ((uval & 0xfff) << 8); } else { @@ -586,31 +630,68 @@ s390_insert_operand (insn, operand, val, file, line) max = (((addressT) 1 << (operand->bits - 1)) << 1) - 1; min = (offsetT) 0; uval = (addressT) val; + + /* Vector register operands have an additional bit in the RXB + field. */ + if (operand->flags & S390_OPERAND_VR) + max = (max << 1) | 1; + /* Length x in an instructions has real length x+1. */ if (operand->flags & S390_OPERAND_LENGTH) uval--; /* Check for underflow / overflow. */ if (uval < min || uval > max) { - const char *err = - "operand out of range (%s not between %ld and %ld)"; - char buf[100]; - if (operand->flags & S390_OPERAND_LENGTH) { uval++; min++; max++; } - sprint_value (buf, uval); - if (file == (char *) NULL) - as_bad (err, buf, (int) min, (int) max); - else - as_bad_where (file, line, err, buf, (int) min, (int) max); + + as_bad_value_out_of_range (_("operand"), uval, (offsetT) min, (offsetT) max, file, line); + return; } } + if (operand->flags & S390_OPERAND_VR) + { + /* Insert the extra bit into the RXB field. */ + switch (operand->shift) + { + case 8: + insn[4] |= (uval & 0x10) >> 1; + break; + case 12: + insn[4] |= (uval & 0x10) >> 2; + break; + case 16: + insn[4] |= (uval & 0x10) >> 3; + break; + case 32: + insn[4] |= (uval & 0x10) >> 4; + break; + } + uval &= 0xf; + } + + if (operand->flags & S390_OPERAND_OR1) + uval |= 1; + if (operand->flags & S390_OPERAND_OR2) + uval |= 2; + if (operand->flags & S390_OPERAND_OR8) + uval |= 8; + + /* Duplicate the operand at bit pos 12 to 16. */ + if (operand->flags & S390_OPERAND_CP16) + { + /* Copy VR operand at bit pos 12 to bit pos 16. */ + insn[2] |= uval << 4; + /* Copy the flag in the RXB field. */ + insn[4] |= (insn[4] & 4) >> 1; + } + /* Insert fragments of the operand byte for byte. */ offset = operand->shift + operand->bits; uval <<= (-offset) & 7; @@ -624,19 +705,14 @@ s390_insert_operand (insn, operand, val, file, line) struct map_tls { - char *string; + const char *string; int length; bfd_reloc_code_real_type reloc; }; -static bfd_reloc_code_real_type s390_tls_suffix - PARAMS ((char **, expressionS *)); - /* Parse tls marker and return the desired relocation. */ static bfd_reloc_code_real_type -s390_tls_suffix (str_p, exp_p) - char **str_p; - expressionS *exp_p; +s390_tls_suffix (char **str_p, expressionS *exp_p) { static struct map_tls mapping[] = { @@ -704,22 +780,15 @@ elf_suffix_type; struct map_bfd { - char *string; + const 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)); - /* Parse @got/@plt/@gotoff. and return the desired relocation. */ static elf_suffix_type -s390_elf_suffix (str_p, exp_p) - char **str_p; - expressionS *exp_p; +s390_elf_suffix (char **str_p, expressionS *exp_p) { static struct map_bfd mapping[] = { @@ -826,9 +895,7 @@ static int lp_count = 0; static int lpe_count = 0; static int -s390_exp_compare (exp1, exp2) - expressionS *exp1; - expressionS *exp2; +s390_exp_compare (expressionS *exp1, expressionS *exp2) { if (exp1->X_op != exp2->X_op) return 0; @@ -880,10 +947,7 @@ s390_exp_compare (exp1, exp2) /* Test for @lit and if its present make an entry in the literal pool and modify the current expression to be an offset into the literal pool. */ static elf_suffix_type -s390_lit_suffix (str_p, exp_p, suffix) - char **str_p; - expressionS *exp_p; - elf_suffix_type suffix; +s390_lit_suffix (char **str_p, expressionS *exp_p, elf_suffix_type suffix) { bfd_reloc_code_real_type reloc; char tmp_name[64]; @@ -968,7 +1032,7 @@ s390_lit_suffix (str_p, exp_p, suffix) } else { - lpe = (struct s390_lpe *) xmalloc (sizeof (struct s390_lpe)); + lpe = XNEW (struct s390_lpe); } lpe->ex = *exp_p; @@ -1026,8 +1090,7 @@ s390_lit_suffix (str_p, exp_p, suffix) /* Like normal .long/.short/.word, except support @got, etc. clobbers input_line_pointer, checks end-of-line. */ static void -s390_elf_cons (nbytes) - register int nbytes; /* 1=.byte, 2=.word, 4=.long */ +s390_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long */) { expressionS exp; elf_suffix_type suffix; @@ -1156,10 +1219,9 @@ struct s390_fixup /* This routine is called for each instruction to be assembled. */ static char * -md_gather_operands (str, insn, opcode) - char *str; - unsigned char *insn; - const struct s390_opcode *opcode; +md_gather_operands (char *str, + unsigned char *insn, + const struct s390_opcode *opcode) { struct s390_fixup fixups[MAX_INSN_FIXUPS]; const struct s390_operand *operand; @@ -1168,14 +1230,12 @@ md_gather_operands (str, insn, opcode) 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; /* Gather the operands. */ @@ -1186,6 +1246,14 @@ md_gather_operands (str, insn, opcode) operand = s390_operands + *opindex_ptr; + if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) && *str == '\0') + { + /* Optional parameters might need to be ORed with a + value so calling s390_insert_operand is needed. */ + s390_insert_operand (insn, operand, 0, NULL, 0); + break; + } + if (skip_optional && (operand->flags & S390_OPERAND_INDEX)) { /* We do an early skip. For D(X,B) constructions the index @@ -1211,7 +1279,24 @@ md_gather_operands (str, insn, opcode) if (ex.X_op == O_illegal) as_bad (_("illegal operand")); else if (ex.X_op == O_absent) - as_bad (_("missing operand")); + { + /* No operands, check if all operands can be skipped. */ + while (*opindex_ptr != 0 && operand->flags & S390_OPERAND_OPTIONAL) + { + if (operand->flags & S390_OPERAND_DISP) + { + /* An optional displacement makes the whole D(X,B) + D(L,B) or D(B) block optional. */ + do { + operand = s390_operands + *(++opindex_ptr); + } while (!(operand->flags & S390_OPERAND_BASE)); + } + operand = s390_operands + *(++opindex_ptr); + } + if (opindex_ptr[0] == '\0') + break; + as_bad (_("missing operand")); + } else if (ex.X_op == O_register || ex.X_op == O_constant) { s390_lit_suffix (&str, &ex, ELF_SUFFIX_NONE); @@ -1229,14 +1314,31 @@ md_gather_operands (str, insn, opcode) } else { + if ((operand->flags & S390_OPERAND_LENGTH) + && ex.X_op != O_constant) + as_fatal (_("invalid length field specified")); if ((operand->flags & S390_OPERAND_INDEX) && ex.X_add_number == 0 && warn_areg_zero) - as_warn ("index register specified but 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"); + as_warn (_("base register specified but zero")); + if ((operand->flags & S390_OPERAND_GPR) + && (operand->flags & S390_OPERAND_REG_PAIR) + && (ex.X_add_number & 1)) + as_fatal (_("odd numbered general purpose register specified as " + "register pair")); + if ((operand->flags & S390_OPERAND_FPR) + && (operand->flags & S390_OPERAND_REG_PAIR) + && ex.X_add_number != 0 && ex.X_add_number != 1 + && ex.X_add_number != 4 && ex.X_add_number != 5 + && ex.X_add_number != 8 && ex.X_add_number != 9 + && ex.X_add_number != 12 && ex.X_add_number != 13) + as_fatal (_("invalid floating point register pair. Valid fp " + "register pair operands are 0, 1, 4, 5, 8, 9, " + "12 or 13.")); s390_insert_operand (insn, operand, ex.X_add_number, NULL, 0); } } @@ -1248,8 +1350,12 @@ md_gather_operands (str, insn, opcode) if (suffix == ELF_SUFFIX_GOT) { - if (operand->flags & S390_OPERAND_DISP) + 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; @@ -1260,8 +1366,14 @@ md_gather_operands (str, insn, opcode) else if (suffix == ELF_SUFFIX_PLT) { if ((operand->flags & S390_OPERAND_PCREL) - && (operand->bits == 16)) + && (operand->bits == 12)) + reloc = BFD_RELOC_390_PLT12DBL; + else if ((operand->flags & S390_OPERAND_PCREL) + && (operand->bits == 16)) reloc = BFD_RELOC_390_PLT16DBL; + else if ((operand->flags & S390_OPERAND_PCREL) + && (operand->bits == 24)) + reloc = BFD_RELOC_390_PLT24DBL; else if ((operand->flags & S390_OPERAND_PCREL) && (operand->bits == 32)) reloc = BFD_RELOC_390_PLT32DBL; @@ -1301,6 +1413,9 @@ md_gather_operands (str, insn, opcode) 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) { @@ -1328,7 +1443,7 @@ md_gather_operands (str, insn, opcode) /* After a displacement a block in parentheses can start. */ if (*str != '(') { - /* Check if parethesed block can be skipped. If the next + /* Check if parenthesized block can be skipped. If the next operand is neiter an optional operand nor a base register then we have a syntax error. */ operand = s390_operands + *(++opindex_ptr); @@ -1339,11 +1454,22 @@ md_gather_operands (str, insn, opcode) while (!(operand->flags & S390_OPERAND_BASE)) operand = s390_operands + *(++opindex_ptr); - /* If there is a next operand it must be seperated by a comma. */ + /* If there is a next operand it must be separated by a comma. */ if (opindex_ptr[1] != '\0') { - if (*str++ != ',') - as_bad (_("syntax error; expected ,")); + if (*str != ',') + { + while (opindex_ptr[1] != '\0') + { + operand = s390_operands + *(++opindex_ptr); + if (operand->flags & S390_OPERAND_OPTIONAL) + continue; + as_bad (_("syntax error; expected ,")); + break; + } + } + else + str++; } } else @@ -1372,11 +1498,22 @@ md_gather_operands (str, insn, opcode) if (*str++ != ')') as_bad (_("syntax error; missing ')' after base register")); skip_optional = 0; - /* If there is a next operand it must be seperated by a comma. */ + /* If there is a next operand it must be separated by a comma. */ if (opindex_ptr[1] != '\0') { - if (*str++ != ',') - as_bad (_("syntax error; expected ,")); + if (*str != ',') + { + while (opindex_ptr[1] != '\0') + { + operand = s390_operands + *(++opindex_ptr); + if (operand->flags & S390_OPERAND_OPTIONAL) + continue; + as_bad (_("syntax error; expected ,")); + break; + } + } + else + str++; } } else @@ -1391,11 +1528,26 @@ md_gather_operands (str, insn, opcode) as_bad (_("syntax error; ')' not allowed here")); str++; } - /* If there is a next operand it must be seperated by a comma. */ + + if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) && *str == '\0') + continue; + + /* If there is a next operand it must be separated by a comma. */ if (opindex_ptr[1] != '\0') { - if (*str++ != ',') - as_bad (_("syntax error; expected ,")); + if (*str != ',') + { + while (opindex_ptr[1] != '\0') + { + operand = s390_operands + *(++opindex_ptr); + if (operand->flags & S390_OPERAND_OPTIONAL) + continue; + as_bad (_("syntax error; expected ,")); + break; + } + } + else + str++; } } } @@ -1438,7 +1590,7 @@ md_gather_operands (str, insn, opcode) BFD_RELOC_UNUSED plus the operand index. This lets us easily handle fixups for any operand type, although that is admittedly not a very exciting feature. We pick a BFD reloc type in - md_apply_fix3. */ + md_apply_fix. */ for (i = 0; i < fc; i++) { @@ -1462,7 +1614,7 @@ md_gather_operands (str, insn, opcode) if (!reloc_howto) abort (); - size = bfd_get_reloc_size (reloc_howto); + size = ((reloc_howto->bitsize - 1) / 8) + 1; if (size < 1 || size > 4) abort (); @@ -1475,6 +1627,7 @@ md_gather_operands (str, insn, opcode) because fixup_segment will signal an overflow for large 4 byte quantities for GOT12 relocations. */ 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; } @@ -1490,8 +1643,7 @@ md_gather_operands (str, insn, opcode) /* This routine is called for each instruction to be assembled. */ void -md_assemble (str) - char *str; +md_assemble (char *str) { const struct s390_opcode *opcode; unsigned char insn[6]; @@ -1512,15 +1664,9 @@ md_assemble (str) } else if (!(opcode->modes & current_mode_mask)) { - as_bad ("Opcode %s not available in this mode", str); + as_bad (_("Opcode %s not available in this mode"), str); return; } - else if (opcode->min_cpu > current_cpu) - { - as_bad ("Opcode %s not available for this cpu", str); - return; - } - memcpy (insn, opcode->opcode, sizeof (insn)); md_gather_operands (s, insn, opcode); } @@ -1549,8 +1695,7 @@ md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol) #endif void -s390_bss (ignore) - int ignore ATTRIBUTE_UNUSED; +s390_bss (int ignore ATTRIBUTE_UNUSED) { /* We don't support putting frags in the BSS segment, we fake it by marking in_bss, then looking at s_skip for clues. */ @@ -1562,8 +1707,7 @@ s390_bss (ignore) /* Pseudo-op handling. */ void -s390_insn (ignore) - int ignore ATTRIBUTE_UNUSED; +s390_insn (int ignore ATTRIBUTE_UNUSED) { expressionS exp; const struct s390_opcode *opformat; @@ -1590,10 +1734,13 @@ s390_insn (ignore) expression (&exp); if (exp.X_op == O_constant) { - if ( (opformat->oplen == 6 && exp.X_op > 0 && exp.X_op < (1ULL << 48)) - || (opformat->oplen == 4 && exp.X_op > 0 && exp.X_op < (1ULL << 32)) - || (opformat->oplen == 2 && exp.X_op > 0 && exp.X_op < (1ULL << 16))) - md_number_to_chars (insn, exp.X_add_number, opformat->oplen); + 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")); } @@ -1603,9 +1750,9 @@ s390_insn (ignore) && opformat->oplen == 6 && generic_bignum[3] == 0) { - md_number_to_chars (insn, generic_bignum[2], 2); - md_number_to_chars (&insn[2], generic_bignum[1], 2); - md_number_to_chars (&insn[4], generic_bignum[0], 2); + 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")); @@ -1629,8 +1776,7 @@ s390_insn (ignore) pseudo-op, but it can also take a single ASCII string. */ static void -s390_byte (ignore) - int ignore ATTRIBUTE_UNUSED; +s390_byte (int ignore ATTRIBUTE_UNUSED) { if (*input_line_pointer != '\"') { @@ -1665,8 +1811,7 @@ s390_byte (ignore) @lit suffix. */ static void -s390_literals (ignore) - int ignore ATTRIBUTE_UNUSED; +s390_literals (int ignore ATTRIBUTE_UNUSED) { struct s390_lpe *lpe; @@ -1724,70 +1869,192 @@ s390_literals (ignore) lpe_count = 0; } -/* 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, or NULL on OK. */ +#define MAX_HISTORY 100 + +/* The .machine pseudo op allows to switch to a different CPU level in + the asm listing. The current CPU setting can be stored on a stack + with .machine push and restored with .machine pop. */ -char * -md_atof (type, litp, sizep) - int type; - char *litp; - int *sizep; +static void +s390_machine (int ignore ATTRIBUTE_UNUSED) { - int prec; - LITTLENUM_TYPE words[4]; - char *t; - int i; + char *cpu_string; + static struct cpu_history + { + unsigned int cpu; + unsigned int flags; + } *cpu_history; + static int curr_hist; + + SKIP_WHITESPACE (); - switch (type) + if (*input_line_pointer == '"') { - case 'f': - prec = 2; - break; + int len; + cpu_string = demand_copy_C_string (&len); + } + else + { + char c; - case 'd': - prec = 4; - break; + cpu_string = input_line_pointer; + do + { + char * str; - default: - *sizep = 0; - return "bad call to md_atof"; + c = get_symbol_name (&str); + c = restore_line_pointer (c); + if (c == '+') + ++ input_line_pointer; + } + while (c == '+'); + + c = *input_line_pointer; + *input_line_pointer = 0; + cpu_string = xstrdup (cpu_string); + (void) restore_line_pointer (c); } - t = atof_ieee (input_line_pointer, type, words); - if (t) - input_line_pointer = t; + if (cpu_string != NULL) + { + unsigned int new_cpu = current_cpu; + unsigned int new_flags = current_flags; + + if (strcmp (cpu_string, "push") == 0) + { + if (cpu_history == NULL) + cpu_history = XNEWVEC (struct cpu_history, MAX_HISTORY); - *sizep = prec * 2; + if (curr_hist >= MAX_HISTORY) + as_bad (_(".machine stack overflow")); + else + { + cpu_history[curr_hist].cpu = current_cpu; + cpu_history[curr_hist].flags = current_flags; + curr_hist++; + } + } + else if (strcmp (cpu_string, "pop") == 0) + { + if (curr_hist <= 0) + as_bad (_(".machine stack underflow")); + else + { + curr_hist--; + new_cpu = cpu_history[curr_hist].cpu; + new_flags = cpu_history[curr_hist].flags; + } + } + else + new_cpu = s390_parse_cpu (cpu_string, &new_flags, TRUE); + + if (new_cpu == S390_OPCODE_MAXCPU) + as_bad (_("invalid machine `%s'"), cpu_string); - for (i = 0; i < prec; i++) + if (new_cpu != current_cpu || new_flags != current_flags) + { + current_cpu = new_cpu; + current_flags = new_flags; + s390_setup_opcodes (); + } + } + + demand_empty_rest_of_line (); +} + +/* The .machinemode pseudo op allows to switch to a different + architecture mode in the asm listing. The current architecture + mode setting can be stored on a stack with .machinemode push and + restored with .machinemode pop. */ + +static void +s390_machinemode (int ignore ATTRIBUTE_UNUSED) +{ + char *mode_string; + static unsigned int *mode_history; + static int curr_hist; + + SKIP_WHITESPACE (); + + { + char c; + + c = get_symbol_name (&mode_string); + mode_string = xstrdup (mode_string); + (void) restore_line_pointer (c); + } + + if (mode_string != NULL) { - md_number_to_chars (litp, (valueT) words[i], 2); - litp += 2; + unsigned int old_mode_mask = current_mode_mask; + char *p; + + for (p = mode_string; *p != 0; p++) + *p = TOLOWER (*p); + + if (strcmp (mode_string, "push") == 0) + { + if (mode_history == NULL) + mode_history = XNEWVEC (unsigned int, MAX_HISTORY); + + if (curr_hist >= MAX_HISTORY) + as_bad (_(".machinemode stack overflow")); + else + mode_history[curr_hist++] = current_mode_mask; + } + else if (strcmp (mode_string, "pop") == 0) + { + if (curr_hist <= 0) + as_bad (_(".machinemode stack underflow")); + else + current_mode_mask = mode_history[--curr_hist]; + } + else + { + if (strcmp (mode_string, "esa") == 0) + current_mode_mask = 1 << S390_OPCODE_ESA; + else if (strcmp (mode_string, "zarch") == 0) + { + if (s390_arch_size == 32) + set_highgprs_p = TRUE; + current_mode_mask = 1 << S390_OPCODE_ZARCH; + } + else if (strcmp (mode_string, "zarch_nohighgprs") == 0) + current_mode_mask = 1 << S390_OPCODE_ZARCH; + else + as_bad (_("invalid machine mode `%s'"), mode_string); + } + + if (current_mode_mask != old_mode_mask) + s390_setup_opcodes (); } - return NULL; + demand_empty_rest_of_line (); +} + +#undef MAX_HISTORY + +const char * +md_atof (int type, char *litp, int *sizep) +{ + return ieee_md_atof (type, litp, sizep, TRUE); } /* Align a section (I don't know why this is machine dependent). */ valueT -md_section_align (seg, addr) - asection *seg; - valueT addr; +md_section_align (asection *seg, valueT addr) { int align = bfd_get_section_alignment (stdoutput, seg); - return ((addr + (1 << align) - 1) & (-1 << align)); + return ((addr + (1 << align) - 1) & -(1 << align)); } /* We don't have any form of relaxing. */ int -md_estimate_size_before_relax (fragp, seg) - fragS *fragp ATTRIBUTE_UNUSED; - asection *seg ATTRIBUTE_UNUSED; +md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED, + asection *seg ATTRIBUTE_UNUSED) { abort (); return 0; @@ -1796,17 +2063,15 @@ md_estimate_size_before_relax (fragp, seg) /* Convert a machine dependent frag. We never generate these. */ void -md_convert_frag (abfd, sec, fragp) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec ATTRIBUTE_UNUSED; - fragS *fragp ATTRIBUTE_UNUSED; +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec ATTRIBUTE_UNUSED, + fragS *fragp ATTRIBUTE_UNUSED) { abort (); } symbolS * -md_undefined_symbol (name) - char *name; +md_undefined_symbol (char *name) { if (*name == '_' && *(name + 1) == 'G' && strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0) @@ -1829,9 +2094,7 @@ md_undefined_symbol (name) given a PC relative reloc. */ long -md_pcrel_from_section (fixp, sec) - fixS *fixp; - segT sec ATTRIBUTE_UNUSED; +md_pcrel_from_section (fixS *fixp, segT sec ATTRIBUTE_UNUSED) { return fixp->fx_frag->fr_address + fixp->fx_where; } @@ -1841,8 +2104,7 @@ md_pcrel_from_section (fixp, sec) to make sure that the dynamic relocations are done correctly, so in some cases we force the original symbol to be used. */ int -tc_s390_fix_adjustable (fixP) - fixS *fixP; +tc_s390_fix_adjustable (fixS *fixP) { /* Don't adjust references to merge sections. */ if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0) @@ -1854,17 +2116,21 @@ tc_s390_fix_adjustable (fixP) || 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_PLT12DBL || fixP->fx_r_type == BFD_RELOC_390_PLT16DBL + || fixP->fx_r_type == BFD_RELOC_390_PLT24DBL || 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 @@ -1874,6 +2140,7 @@ tc_s390_fix_adjustable (fixP) || 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 @@ -1897,14 +2164,14 @@ tc_s390_fix_adjustable (fixP) /* Return true if we must always emit a reloc for a type and false if there is some hope of resolving it at assembly time. */ int -tc_s390_force_relocation (fixp) - struct fix *fixp; +tc_s390_force_relocation (struct fix *fixp) { /* Ensure we emit a relocation for every reference to the global offset table or to the procedure link table. */ 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: @@ -1917,17 +2184,20 @@ tc_s390_force_relocation (fixp) case BFD_RELOC_390_GOT64: case BFD_RELOC_390_GOTENT: case BFD_RELOC_390_PLT32: + case BFD_RELOC_390_PLT12DBL: case BFD_RELOC_390_PLT16DBL: + case BFD_RELOC_390_PLT24DBL: 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;; + break; } return generic_force_reloc (fixp); @@ -1943,10 +2213,7 @@ tc_s390_force_relocation (fixp) fixup. */ void -md_apply_fix3 (fixP, valP, seg) - fixS *fixP; - valueT *valP; - segT seg ATTRIBUTE_UNUSED; +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { char *where; valueT value = *valP; @@ -1955,7 +2222,7 @@ md_apply_fix3 (fixP, valP, seg) if (fixP->fx_subsy != NULL) as_bad_where (fixP->fx_file, fixP->fx_line, - "cannot emit relocation %s against subsy symbol %s", + _("cannot emit relocation %s against subsy symbol %s"), bfd_get_reloc_code_name (fixP->fx_r_type), S_GET_NAME (fixP->fx_subsy)); @@ -1978,8 +2245,8 @@ md_apply_fix3 (fixP, valP, seg) if (fixP->fx_done) { /* Insert the fully resolved operand value. */ - s390_insert_operand (where, operand, (offsetT) value, - fixP->fx_file, fixP->fx_line); + s390_insert_operand ((unsigned char *) where, operand, + (offsetT) value, fixP->fx_file, fixP->fx_line); return; } @@ -1999,12 +2266,26 @@ md_apply_fix3 (fixP, valP, seg) 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 == 12 && operand->shift == 12 + && (operand->flags & S390_OPERAND_PCREL)) + { + fixP->fx_size = 2; + fixP->fx_where += 1; + fixP->fx_offset += 1; + fixP->fx_r_type = BFD_RELOC_390_PC12DBL; + } else if (operand->bits == 16 && operand->shift == 16) { fixP->fx_size = 2; @@ -2017,6 +2298,14 @@ md_apply_fix3 (fixP, valP, seg) else fixP->fx_r_type = BFD_RELOC_16; } + else if (operand->bits == 24 && operand->shift == 24 + && (operand->flags & S390_OPERAND_PCREL)) + { + fixP->fx_size = 3; + fixP->fx_where += 3; + fixP->fx_offset += 3; + fixP->fx_r_type = BFD_RELOC_390_PC24DBL; + } else if (operand->bits == 32 && operand->shift == 16 && (operand->flags & S390_OPERAND_PCREL)) { @@ -2027,7 +2316,7 @@ md_apply_fix3 (fixP, valP, seg) } else { - char *sfile; + const char *sfile; unsigned int sline; /* Use expr_symbol_where to see if this is an expression @@ -2055,23 +2344,44 @@ md_apply_fix3 (fixP, valP, seg) case BFD_RELOC_390_12: case BFD_RELOC_390_GOT12: case BFD_RELOC_390_GOTPLT12: + case BFD_RELOC_390_PC12DBL: + case BFD_RELOC_390_PLT12DBL: + if (fixP->fx_pcrel) + value++; + if (fixP->fx_done) { unsigned short mop; + if (fixP->fx_pcrel) + value >>= 1; + 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", + _("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 @@ -2093,6 +2403,20 @@ md_apply_fix3 (fixP, valP, seg) md_number_to_chars (where, (offsetT) value >> 1, 2); break; + case BFD_RELOC_390_PC24DBL: + case BFD_RELOC_390_PLT24DBL: + value += 3; + if (fixP->fx_done) + { + unsigned int mop; + value >>= 1; + + mop = bfd_getb32 ((unsigned char *) where - 1); + mop |= (unsigned int) (value & 0xffffff); + bfd_putb32 ((bfd_vma) mop, (unsigned char *) where - 1); + } + break; + case BFD_RELOC_32: if (fixP->fx_pcrel) fixP->fx_r_type = BFD_RELOC_32_PCREL; @@ -2168,6 +2492,7 @@ md_apply_fix3 (fixP, valP, seg) 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: @@ -2181,10 +2506,12 @@ md_apply_fix3 (fixP, valP, seg) 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); /* Fully resolved at link time. */ break; case BFD_RELOC_390_TLS_IEENT: /* Fully resolved at link time. */ + S_SET_THREAD_LOCAL (fixP->fx_addsy); value += 2; break; @@ -2193,11 +2520,9 @@ md_apply_fix3 (fixP, valP, seg) 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); + as_fatal (_("Gas failure, reloc type %s\n"), reloc_name); else - fprintf (stderr, "Gas failure, reloc type #%i\n", fixP->fx_r_type); - fflush (stderr); - abort (); + as_fatal (_("Gas failure, reloc type #%i\n"), fixP->fx_r_type); } } @@ -2208,9 +2533,7 @@ md_apply_fix3 (fixP, valP, seg) /* Generate a reloc for a fixup. */ arelent * -tc_gen_reloc (seg, fixp) - asection *seg ATTRIBUTE_UNUSED; - fixS *fixp; +tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp) { bfd_reloc_code_real_type code; arelent *reloc; @@ -2225,8 +2548,8 @@ tc_gen_reloc (seg, fixp) code = BFD_RELOC_390_GOTPCDBL; } - reloc = (arelent *) xmalloc (sizeof (arelent)); - reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + reloc = XNEW (arelent); + reloc->sym_ptr_ptr = XNEW (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); @@ -2237,9 +2560,40 @@ tc_gen_reloc (seg, fixp) bfd_get_reloc_code_name (code)); /* Set howto to a garbage value so that we can keep going. */ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32); - assert (reloc->howto != NULL); + gas_assert (reloc->howto != NULL); } reloc->addend = fixp->fx_offset; return reloc; } + +void +s390_cfi_frame_initial_instructions (void) +{ + cfi_add_CFA_def_cfa (15, s390_arch_size == 64 ? 160 : 96); +} + +int +tc_s390_regname_to_dw2regnum (char *regname) +{ + int regnum = -1; + + if (regname[0] != 'c' && regname[0] != 'a') + { + regnum = reg_name_search (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; +} + +void +s390_elf_final_processing (void) +{ + if (set_highgprs_p) + elf_elfheader (stdoutput)->e_flags |= EF_S390_HIGH_GPRS; +}