X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-s390.c;h=25cc4ef15616fe26f337042352d657da46136fe6;hb=33eaf5de31b248f84ae108cf0cf4e1664db9ee51;hp=bce0604f61aea3c5c7d6f8e617deafdf8f3ce47a;hpb=2ee563b53258d390d7446e90a67f465d504ae44c;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-s390.c b/gas/config/tc-s390.c index bce0604f61..25cc4ef156 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 Free Software Foundation, Inc. + Copyright (C) 2000-2017 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,14 +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" @@ -32,23 +33,33 @@ #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; -/* Current architecture. Start with the smallest instruction set. */ -static enum s390_opcode_arch_val current_architecture = S390_OPCODE_ESA; -static int current_arch_mask = 1 << S390_OPCODE_ESA; -static int current_arch_requested = 0; - -/* Whether to use user friendly register names. Default is true. */ +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; +/* All facilities are enabled by default. */ +static unsigned int current_flags = S390_INSTR_FLAG_FACILITY_MASK; +/* The mode mask default is picked in init_default_arch depending on + the current cpu. */ +static unsigned int current_mode_mask = 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 -#define TARGET_REG_NAMES_P true +#define TARGET_REG_NAMES_P TRUE #endif -static boolean reg_names_p = TARGET_REG_NAMES_P; +static bfd_boolean reg_names_p = TARGET_REG_NAMES_P; /* Set to TRUE if we want to warn about zero base/index registers. */ -static boolean warn_areg_zero = FALSE; +static bfd_boolean warn_areg_zero = FALSE; /* Generic assembler global variables which must be defined by all targets. */ @@ -70,176 +81,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 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; } @@ -255,9 +157,8 @@ reg_name_search (regs, regcount, name) * original state. */ -static boolean -register_name (expressionP) - expressionS *expressionP; +static bfd_boolean +register_name (expressionS *expressionP) { int reg_number; char *name; @@ -269,13 +170,13 @@ register_name (expressionP) if (name[0] == '%' && ISALPHA (name[1])) name = ++input_line_pointer; else - return false; + 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) @@ -286,12 +187,12 @@ register_name (expressionP) /* Make the rest nice. */ expressionP->X_add_symbol = NULL; expressionP->X_op_symbol = NULL; - return true; + return TRUE; } /* Reset the line as if we had not done anything. */ input_line_pointer = start; - return false; + return FALSE; } /* Local variables. */ @@ -300,7 +201,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 +209,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:"; @@ -319,44 +220,176 @@ struct option md_longopts[] = { size_t md_longopts_size = sizeof (md_longopts); /* Initialize the default opcode arch and word size from the default - architecture name. */ + architecture name if not specified by an option. */ static void -init_default_arch () +init_default_arch (void) { - if (current_arch_requested) - return; - if (strcmp (default_arch, "s390") == 0) { - s390_arch_size = 32; - current_architecture = S390_OPCODE_ESA; + if (s390_arch_size == 0) + s390_arch_size = 32; } else if (strcmp (default_arch, "s390x") == 0) { - s390_arch_size = 64; - current_architecture = S390_OPCODE_ESAME; + if (s390_arch_size == 0) + s390_arch_size = 64; } else - as_fatal ("Invalid default architecture, broken assembler."); - current_arch_mask = 1 << current_architecture; + 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. */ - if (! s390_arch_size) - init_default_arch (); + init_default_arch (); 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 name_len; + const char * alt_name; + unsigned int alt_name_len; + unsigned int flags; + } cpu_table[S390_OPCODE_MAXCPU] = + { + { STRING_COMMA_LEN ("g5"), STRING_COMMA_LEN ("arch3"), 0 }, + { STRING_COMMA_LEN ("g6"), STRING_COMMA_LEN (""), 0 }, + { STRING_COMMA_LEN ("z900"), STRING_COMMA_LEN ("arch5"), 0 }, + { STRING_COMMA_LEN ("z990"), STRING_COMMA_LEN ("arch6"), 0 }, + { STRING_COMMA_LEN ("z9-109"), STRING_COMMA_LEN (""), 0 }, + { STRING_COMMA_LEN ("z9-ec"), STRING_COMMA_LEN ("arch7"), 0 }, + { STRING_COMMA_LEN ("z10"), STRING_COMMA_LEN ("arch8"), 0 }, + { STRING_COMMA_LEN ("z196"), STRING_COMMA_LEN ("arch9"), 0 }, + { STRING_COMMA_LEN ("zEC12"), STRING_COMMA_LEN ("arch10"), + S390_INSTR_FLAG_HTM }, + { STRING_COMMA_LEN ("z13"), STRING_COMMA_LEN ("arch11"), + 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_alt; + + l = cpu_table[icpu].name_len; + + if (strncmp (arg, cpu_table[icpu].name, l) == 0 + && (arg[l] == 0 || arg[l] == '+')) + { + arg += l; + break; + } + + l_alt = cpu_table[icpu].alt_name_len; + + if (l_alt > 0 + && strncmp (arg, cpu_table[icpu].alt_name, l_alt) == 0 + && (arg[l_alt] == 0 || arg[l_alt] == '+')) + { + arg += l_alt; + break; + } + } + } + + if (icpu == S390_OPCODE_MAXCPU) + return S390_OPCODE_MAXCPU; + + 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) { @@ -365,20 +398,40 @@ md_parse_option (c, arg) break; case 'm': if (arg != NULL && strcmp (arg, "regnames") == 0) - reg_names_p = true; + reg_names_p = TRUE; else if (arg != NULL && strcmp (arg, "no-regnames") == 0) - reg_names_p = false; + 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 = 31; + 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) + { + 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) + { + 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; + } + } + else { as_bad (_("invalid switch -m%s"), arg); @@ -387,14 +440,13 @@ md_parse_option (c, arg) break; case 'A': + /* Option -A is deprecated. Still available for compatibility. */ if (arg != NULL && strcmp (arg, "esa") == 0) - current_architecture = S390_OPCODE_ESA; + current_cpu = S390_OPCODE_G5; else if (arg != NULL && strcmp (arg, "esame") == 0) - current_architecture = S390_OPCODE_ESAME; + current_cpu = S390_OPCODE_Z900; else - as_bad ("invalid architecture -A%s", arg); - current_arch_mask = 1 << current_architecture; - current_arch_requested = 1; + as_bad (_("invalid architecture -A%s"), arg); break; /* -V: SVR4 argument to print version ID. */ @@ -415,8 +467,7 @@ md_parse_option (c, arg) } void -md_show_usage (stream) - FILE *stream; +md_show_usage (FILE *stream) { fprintf (stream, _("\ S390 options:\n\ @@ -430,21 +481,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; - 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_arch_mask == (1 << S390_OPCODE_ESA)) - as_warn ("The 64 bit file format is used without esame instructions."); + 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; /* Set the ELF flags if desired. */ if (s390_flags) @@ -456,41 +574,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); @@ -498,28 +597,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; @@ -537,7 +622,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) @@ -555,6 +640,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 { @@ -563,31 +651,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; @@ -599,34 +724,92 @@ s390_insert_operand (insn, operand, val, file, line) } } +struct map_tls + { + const char *string; + int length; + bfd_reloc_code_real_type reloc; + }; + +/* Parse tls marker and return the desired relocation. */ +static bfd_reloc_code_real_type +s390_tls_suffix (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) + { + /* Found a matching tls suffix. */ + *str_p = str; + return ptr->reloc; + } + return BFD_RELOC_UNUSED; +} + /* Structure used to hold suffixes. */ typedef enum { ELF_SUFFIX_NONE = 0, ELF_SUFFIX_GOT, ELF_SUFFIX_PLT, - ELF_SUFFIX_GOTENT + 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; + 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[] = { @@ -634,6 +817,15 @@ s390_elf_suffix (str_p, exp_p) { "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 } }; @@ -724,9 +916,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; @@ -739,6 +929,7 @@ s390_exp_compare (exp1, exp2) case O_big: as_bad (_("Can't handle O_big in s390_exp_compare")); + return 0; case O_symbol: /* X_add_symbol & X_add_number must be equal. */ case O_symbol_rva: @@ -775,13 +966,10 @@ s390_exp_compare (exp1, exp2) } } -/* Test for @lit and if its present make an entry in the literal pool and +/* Test for @lit and if it's 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]; @@ -866,7 +1054,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; @@ -908,7 +1096,7 @@ s390_lit_suffix (str_p, exp_p, suffix) } /* Now change exp_p to the offset into the literal pool. - Thats the expression: .L^Ax^By-.L^Ax */ + That's the expression: .L^Ax^By-.L^Ax */ exp_p->X_add_symbol = lpe->sym; exp_p->X_op_symbol = lp_sym; exp_p->X_op = O_subtract; @@ -924,8 +1112,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; @@ -949,22 +1136,72 @@ s390_elf_cons (nbytes) int size; char *where; - if (nbytes == 2 && suffix == ELF_SUFFIX_GOT) - reloc = BFD_RELOC_390_GOT16; - else if (nbytes == 4 && suffix == ELF_SUFFIX_GOT) - reloc = BFD_RELOC_32_GOT_PCREL; - else if (nbytes == 8 && suffix == ELF_SUFFIX_GOT) - reloc = BFD_RELOC_390_GOT64; - else if (nbytes == 4 && suffix == ELF_SUFFIX_PLT) - reloc = BFD_RELOC_390_PLT32; - else if (nbytes == 8 && suffix == ELF_SUFFIX_PLT) - reloc = BFD_RELOC_390_PLT64; + if (nbytes == 2) + { + static bfd_reloc_code_real_type tab2[] = + { + BFD_RELOC_UNUSED, /* ELF_SUFFIX_NONE */ + BFD_RELOC_390_GOT16, /* ELF_SUFFIX_GOT */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_PLT */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTENT */ + BFD_RELOC_16_GOTOFF, /* ELF_SUFFIX_GOTOFF */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTPLT */ + BFD_RELOC_390_PLTOFF16, /* ELF_SUFFIX_PLTOFF */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_GD */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_GOTIE */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_IE */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_LDM */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_LDO */ + BFD_RELOC_UNUSED /* ELF_SUFFIX_TLS_LE */ + }; + reloc = tab2[suffix]; + } + else if (nbytes == 4) + { + static bfd_reloc_code_real_type tab4[] = + { + BFD_RELOC_UNUSED, /* ELF_SUFFIX_NONE */ + BFD_RELOC_32_GOT_PCREL, /* ELF_SUFFIX_GOT */ + BFD_RELOC_390_PLT32, /* ELF_SUFFIX_PLT */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTENT */ + BFD_RELOC_32_GOTOFF, /* ELF_SUFFIX_GOTOFF */ + BFD_RELOC_390_GOTPLT32, /* ELF_SUFFIX_GOTPLT */ + BFD_RELOC_390_PLTOFF32, /* ELF_SUFFIX_PLTOFF */ + BFD_RELOC_390_TLS_GD32, /* ELF_SUFFIX_TLS_GD */ + BFD_RELOC_390_TLS_GOTIE32, /* ELF_SUFFIX_TLS_GOTIE */ + BFD_RELOC_390_TLS_IE32, /* ELF_SUFFIX_TLS_IE */ + BFD_RELOC_390_TLS_LDM32, /* ELF_SUFFIX_TLS_LDM */ + BFD_RELOC_390_TLS_LDO32, /* ELF_SUFFIX_TLS_LDO */ + BFD_RELOC_390_TLS_LE32 /* ELF_SUFFIX_TLS_LE */ + }; + reloc = tab4[suffix]; + } + else if (nbytes == 8) + { + static bfd_reloc_code_real_type tab8[] = + { + BFD_RELOC_UNUSED, /* ELF_SUFFIX_NONE */ + BFD_RELOC_390_GOT64, /* ELF_SUFFIX_GOT */ + BFD_RELOC_390_PLT64, /* ELF_SUFFIX_PLT */ + BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTENT */ + BFD_RELOC_390_GOTOFF64, /* ELF_SUFFIX_GOTOFF */ + BFD_RELOC_390_GOTPLT64, /* ELF_SUFFIX_GOTPLT */ + BFD_RELOC_390_PLTOFF64, /* ELF_SUFFIX_PLTOFF */ + BFD_RELOC_390_TLS_GD64, /* ELF_SUFFIX_TLS_GD */ + BFD_RELOC_390_TLS_GOTIE64, /* ELF_SUFFIX_TLS_GOTIE */ + BFD_RELOC_390_TLS_IE64, /* ELF_SUFFIX_TLS_IE */ + BFD_RELOC_390_TLS_LDM64, /* ELF_SUFFIX_TLS_LDM */ + BFD_RELOC_390_TLS_LDO64, /* ELF_SUFFIX_TLS_LDO */ + BFD_RELOC_390_TLS_LE64 /* ELF_SUFFIX_TLS_LE */ + }; + reloc = tab8[suffix]; + } else reloc = BFD_RELOC_UNUSED; - if (reloc != BFD_RELOC_UNUSED) + if (reloc != BFD_RELOC_UNUSED + && (reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc))) { - 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"), @@ -972,9 +1209,9 @@ s390_elf_cons (nbytes) where = frag_more (nbytes); md_number_to_chars (where, 0, size); /* To make fixup_segment do the pc relative conversion the - pcrel parameter on the fix_new_exp call needs to be false. */ + pcrel parameter on the fix_new_exp call needs to be FALSE. */ fix_new_exp (frag_now, where - frag_now->fr_literal, - size, &exp, false, reloc); + size, &exp, FALSE, reloc); } else as_bad (_("relocation not applicable")); @@ -1004,36 +1241,41 @@ 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; 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; /* Gather the operands. */ fc = 0; for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) { - expressionS ex; char *hold; 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 @@ -1059,7 +1301,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); @@ -1077,14 +1336,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 == TRUE) - as_warn ("index register specified but zero"); + && warn_areg_zero) + as_warn (_("index register specified but zero")); if ((operand->flags & S390_OPERAND_BASE) && ex.X_add_number == 0 - && warn_areg_zero == TRUE) - as_warn ("base register specified but zero"); + && warn_areg_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); } } @@ -1096,8 +1372,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; @@ -1108,8 +1388,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; @@ -1120,6 +1406,45 @@ md_gather_operands (str, insn, opcode) && (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")); @@ -1140,8 +1465,8 @@ 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 - operand is neiter an optional operand nor a base register + /* Check if parenthesized block can be skipped. If the next + operand is neither an optional operand nor a base register then we have a syntax error. */ operand = s390_operands + *(++opindex_ptr); if (!(operand->flags & (S390_OPERAND_INDEX|S390_OPERAND_BASE))) @@ -1151,11 +1476,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 @@ -1180,15 +1516,26 @@ md_gather_operands (str, insn, opcode) } else if (operand->flags & S390_OPERAND_BASE) { - /* After the base register the parenthesed block ends. */ + /* After the base register the parenthesised block ends. */ 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 @@ -1203,11 +1550,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++; } } } @@ -1215,6 +1577,20 @@ md_gather_operands (str, insn, opcode) while (ISSPACE (*str)) ++str; + /* Check for tls instruction marker. */ + reloc = s390_tls_suffix (&str, &ex); + if (reloc != BFD_RELOC_UNUSED) + { + /* We need to generate a fixup of type 'reloc' for this + instruction. */ + 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; @@ -1236,9 +1612,18 @@ 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++) { + + if (fixups[i].opindex < 0) + { + /* Create tls instruction marker relocation. */ + 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) @@ -1251,7 +1636,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 (); @@ -1264,8 +1649,12 @@ 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; + + if (operand->flags & S390_OPERAND_PCREL) + fixP->fx_pcrel_adjust = operand->shift / 8; } else fix_new_exp (frag_now, f - frag_now->fr_literal, 4, &fixups[i].exp, @@ -1279,8 +1668,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]; @@ -1299,12 +1687,11 @@ md_assemble (str) as_bad (_("Unrecognized opcode: `%s'"), str); return; } - else if (!(opcode->architecture & current_arch_mask)) + else if (!(opcode->modes & current_mode_mask)) { - as_bad ("Opcode %s not available in this architecture", str); + as_bad (_("Opcode %s not available in this mode"), str); return; } - memcpy (insn, opcode->opcode, sizeof (insn)); md_gather_operands (s, insn, opcode); } @@ -1333,8 +1720,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. */ @@ -1346,8 +1732,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; @@ -1374,10 +1759,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")); } @@ -1387,9 +1775,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")); @@ -1413,8 +1801,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 != '\"') { @@ -1449,8 +1836,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; @@ -1508,70 +1894,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); + + 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); - *sizep = prec * 2; + if (new_cpu != current_cpu || new_flags != current_flags) + { + current_cpu = new_cpu; + current_flags = new_flags; + s390_setup_opcodes (); + } + } - for (i = 0; i < prec; i++) + 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; @@ -1580,17 +2088,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) @@ -1613,9 +2119,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; } @@ -1625,28 +2129,57 @@ 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) { - /* Prevent all adjustments to global symbols. */ - if (S_IS_EXTERN (fixP->fx_addsy)) - return 0; - if (S_IS_WEAK (fixP->fx_addsy)) - return 0; /* Don't adjust references to merge sections. */ if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0) return 0; /* adjust_reloc_syms doesn't know about the GOT. */ - if ( fixP->fx_r_type == BFD_RELOC_32_GOTOFF + 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_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 + || 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; @@ -1654,33 +2187,45 @@ 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 a assembly time. */ + 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: + 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_PLT12DBL: case BFD_RELOC_390_PLT16DBL: + case BFD_RELOC_390_PLT24DBL: case BFD_RELOC_390_PLT32DBL: case BFD_RELOC_390_PLT64: - case BFD_RELOC_VTABLE_INHERIT: - case BFD_RELOC_VTABLE_ENTRY: + 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: - return 0; + break; } + + return generic_force_reloc (fixp); } /* Apply a fixup to the object code. This is called for all the @@ -1693,10 +2238,7 @@ tc_s390_force_relocation (fixp) fixup. */ void -md_apply_fix3 (fixP, valP, seg) - fixS *fixP; - valueT *valP; - segT seg; +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { char *where; valueT value = *valP; @@ -1704,35 +2246,13 @@ md_apply_fix3 (fixP, valP, seg) where = fixP->fx_frag->fr_literal + fixP->fx_where; if (fixP->fx_subsy != NULL) - { - if ((fixP->fx_addsy != NULL - && S_GET_SEGMENT (fixP->fx_addsy) == S_GET_SEGMENT (fixP->fx_subsy) - && SEG_NORMAL (S_GET_SEGMENT (fixP->fx_addsy))) - || (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)) - value += S_GET_VALUE (fixP->fx_subsy); - if (!S_IS_DEFINED (fixP->fx_subsy)) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("unresolved fx_subsy symbol that must be resolved")); - value -= S_GET_VALUE (fixP->fx_subsy); - - if (S_GET_SEGMENT (fixP->fx_subsy) == seg && ! fixP->fx_pcrel) - value += MD_PCREL_FROM_SECTION (fixP, seg); - } + 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_subsy != NULL - && S_GET_SEGMENT (fixP->fx_addsy) == S_GET_SEGMENT (fixP->fx_subsy) - && SEG_NORMAL (S_GET_SEGMENT (fixP->fx_addsy))) - || (S_GET_SEGMENT (fixP->fx_addsy) == seg - && fixP->fx_pcrel && TC_RELOC_RTSYM_LOC_FIXUP (fixP)) - || (!fixP->fx_pcrel - && S_GET_SEGMENT (fixP->fx_addsy) == absolute_section) - || (S_GET_SEGMENT (fixP->fx_addsy) != undefined_section - && !bfd_is_com_section (S_GET_SEGMENT (fixP->fx_addsy)) - && TC_FIX_ADJUSTABLE (fixP))) - value -= S_GET_VALUE (fixP->fx_addsy); - if (fixP->fx_pcrel) value += fixP->fx_frag->fr_address + fixP->fx_where; } @@ -1750,8 +2270,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; } @@ -1771,12 +2291,27 @@ 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_pcrel_adjust = 1; + fixP->fx_r_type = BFD_RELOC_390_PC12DBL; + } else if (operand->bits == 16 && operand->shift == 16) { fixP->fx_size = 2; @@ -1785,21 +2320,41 @@ md_apply_fix3 (fixP, valP, seg) { fixP->fx_r_type = BFD_RELOC_390_PC16DBL; fixP->fx_offset += 2; + fixP->fx_pcrel_adjust = 2; } else fixP->fx_r_type = BFD_RELOC_16; } + else if (operand->bits == 16 && operand->shift == 32 + && (operand->flags & S390_OPERAND_PCREL)) + { + fixP->fx_size = 2; + fixP->fx_where += 4; + fixP->fx_offset += 4; + fixP->fx_pcrel_adjust = 4; + fixP->fx_r_type = BFD_RELOC_390_PC16DBL; + } + 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_pcrel_adjust = 3; + fixP->fx_r_type = BFD_RELOC_390_PC24DBL; + } 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_pcrel_adjust = 2; fixP->fx_r_type = BFD_RELOC_390_PC32DBL; } else { - char *sfile; + const char *sfile; unsigned int sline; /* Use expr_symbol_where to see if this is an expression @@ -1826,23 +2381,45 @@ md_apply_fix3 (fixP, valP, seg) break; 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 += fixP->fx_pcrel_adjust; + 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 @@ -1852,16 +2429,32 @@ md_apply_fix3 (fixP, valP, seg) 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; + value += fixP->fx_pcrel_adjust; if (fixP->fx_done) md_number_to_chars (where, (offsetT) value >> 1, 2); break; + case BFD_RELOC_390_PC24DBL: + case BFD_RELOC_390_PLT24DBL: + value += fixP->fx_pcrel_adjust; + 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; @@ -1877,7 +2470,9 @@ md_apply_fix3 (fixP, valP, seg) 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; @@ -1885,7 +2480,8 @@ md_apply_fix3 (fixP, valP, seg) case BFD_RELOC_390_PLT32DBL: case BFD_RELOC_390_GOTPCDBL: case BFD_RELOC_390_GOTENT: - value += 2; + case BFD_RELOC_390_GOTPLTENT: + value += fixP->fx_pcrel_adjust; if (fixP->fx_done) md_number_to_chars (where, (offsetT) value >> 1, 4); break; @@ -1895,8 +2491,15 @@ md_apply_fix3 (fixP, valP, seg) 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; @@ -1921,16 +2524,43 @@ md_apply_fix3 (fixP, valP, seg) 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); + /* 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; + 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); + 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); } } @@ -1941,9 +2571,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; @@ -1958,8 +2586,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); @@ -1970,9 +2598,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; +}