123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- /* s390-dis.c -- Disassemble S390 instructions
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
- Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
- This file is part of the GNU opcodes library.
- This library 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 3, or (at your option)
- any later version.
- It is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- License for more details.
- You should have received a copy of the GNU General Public License
- along with this file; see the file COPYING. If not, write to the
- Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- #include "sysdep.h"
- #include <stdio.h>
- #include "ansidecl.h"
- #include "dis-asm.h"
- #include "opintl.h"
- #include "opcode/s390.h"
- static int init_flag = 0;
- static int opc_index[256];
- static int current_arch_mask = 0;
- /* Set up index table for first opcode byte. */
- static void
- init_disasm (struct disassemble_info *info)
- {
- int i;
- const char *p;
- memset (opc_index, 0, sizeof (opc_index));
- /* Reverse order, such that each opc_index ends up pointing to the
- first matching entry instead of the last. */
- for (i = s390_num_opcodes; i--; )
- opc_index[s390_opcodes[i].opcode[0]] = i;
- for (p = info->disassembler_options; p != NULL; )
- {
- if (CONST_STRNEQ (p, "esa"))
- current_arch_mask = 1 << S390_OPCODE_ESA;
- else if (CONST_STRNEQ (p, "zarch"))
- current_arch_mask = 1 << S390_OPCODE_ZARCH;
- else
- fprintf (stderr, "Unknown S/390 disassembler option: %s\n", p);
- p = strchr (p, ',');
- if (p != NULL)
- p++;
- }
- if (!current_arch_mask)
- current_arch_mask = 1 << S390_OPCODE_ZARCH;
- init_flag = 1;
- }
- /* Derive the length of an instruction from its first byte. */
- static inline int
- s390_insn_length (const bfd_byte *buffer)
- {
- /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6. */
- return ((buffer[0] >> 6) + 3) & ~1U;
- }
- /* Match the instruction in BUFFER against the given OPCODE, excluding
- the first byte. */
- static inline int
- s390_insn_matches_opcode (const bfd_byte *buffer,
- const struct s390_opcode *opcode)
- {
- return (buffer[1] & opcode->mask[1]) == opcode->opcode[1]
- && (buffer[2] & opcode->mask[2]) == opcode->opcode[2]
- && (buffer[3] & opcode->mask[3]) == opcode->opcode[3]
- && (buffer[4] & opcode->mask[4]) == opcode->opcode[4]
- && (buffer[5] & opcode->mask[5]) == opcode->opcode[5];
- }
- union operand_value
- {
- int i;
- unsigned int u;
- };
- /* Extracts an operand value from an instruction. */
- /* We do not perform the shift operation for larl-type address
- operands here since that would lead to an overflow of the 32 bit
- integer value. Instead the shift operation is done when printing
- the operand. */
- static inline union operand_value
- s390_extract_operand (const bfd_byte *insn,
- const struct s390_operand *operand)
- {
- union operand_value ret;
- unsigned int val;
- int bits;
- const bfd_byte *orig_insn = insn;
- /* Extract fragments of the operand byte for byte. */
- insn += operand->shift / 8;
- bits = (operand->shift & 7) + operand->bits;
- val = 0;
- do
- {
- val <<= 8;
- val |= (unsigned int) *insn++;
- bits -= 8;
- }
- while (bits > 0);
- val >>= -bits;
- val &= ((1U << (operand->bits - 1)) << 1) - 1;
- /* Check for special long displacement case. */
- if (operand->bits == 20 && operand->shift == 20)
- val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
- /* Sign extend value if the operand is signed or pc relative. Avoid
- integer overflows. */
- if (operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL))
- {
- unsigned int m = 1U << (operand->bits - 1);
- if (val >= m)
- ret.i = (int) (val - m) - 1 - (int) (m - 1U);
- else
- ret.i = (int) val;
- }
- else if (operand->flags & S390_OPERAND_LENGTH)
- /* Length x in an instruction has real length x + 1. */
- ret.u = val + 1;
- else if (operand->flags & S390_OPERAND_VR)
- {
- /* Extract the extra bits for a vector register operand stored
- in the RXB field. */
- unsigned vr = operand->shift == 32 ? 3
- : (unsigned) operand->shift / 4 - 2;
- ret.u = val | ((orig_insn[4] & (1 << (3 - vr))) << (vr + 1));
- }
- else
- ret.u = val;
- return ret;
- }
- /* Print the S390 instruction in BUFFER, assuming that it matches the
- given OPCODE. */
- static void
- s390_print_insn_with_opcode (bfd_vma memaddr,
- struct disassemble_info *info,
- const bfd_byte *buffer,
- const struct s390_opcode *opcode)
- {
- const unsigned char *opindex;
- char separator;
- /* Mnemonic. */
- info->fprintf_func (info->stream, "%s", opcode->name);
- /* Operands. */
- separator = '\t';
- for (opindex = opcode->operands; *opindex != 0; opindex++)
- {
- const struct s390_operand *operand = s390_operands + *opindex;
- union operand_value val = s390_extract_operand (buffer, operand);
- unsigned long flags = operand->flags;
- if ((flags & S390_OPERAND_INDEX) && val.u == 0)
- continue;
- if ((flags & S390_OPERAND_BASE) &&
- val.u == 0 && separator == '(')
- {
- separator = ',';
- continue;
- }
- /* For instructions with a last optional operand don't print it
- if zero. */
- if ((opcode->flags & S390_INSTR_FLAG_OPTPARM)
- && val.u == 0
- && opindex[1] == 0)
- break;
- if (flags & S390_OPERAND_GPR)
- info->fprintf_func (info->stream, "%c%%r%u", separator, val.u);
- else if (flags & S390_OPERAND_FPR)
- info->fprintf_func (info->stream, "%c%%f%u", separator, val.u);
- else if (flags & S390_OPERAND_VR)
- info->fprintf_func (info->stream, "%c%%v%i", separator, val.u);
- else if (flags & S390_OPERAND_AR)
- info->fprintf_func (info->stream, "%c%%a%u", separator, val.u);
- else if (flags & S390_OPERAND_CR)
- info->fprintf_func (info->stream, "%c%%c%u", separator, val.u);
- else if (flags & S390_OPERAND_PCREL)
- {
- info->fprintf_func (info->stream, "%c", separator);
- info->print_address_func (memaddr + val.i + val.i, info);
- }
- else if (flags & S390_OPERAND_SIGNED)
- info->fprintf_func (info->stream, "%c%i", separator, val.i);
- else
- {
- if (flags & S390_OPERAND_OR1)
- val.u &= ~1;
- if (flags & S390_OPERAND_OR2)
- val.u &= ~2;
- if (flags & S390_OPERAND_OR8)
- val.u &= ~8;
- if ((opcode->flags & S390_INSTR_FLAG_OPTPARM)
- && val.u == 0
- && opindex[1] == 0)
- break;
- info->fprintf_func (info->stream, "%c%u", separator, val.u);
- }
- if (flags & S390_OPERAND_DISP)
- separator = '(';
- else if (flags & S390_OPERAND_BASE)
- {
- info->fprintf_func (info->stream, ")");
- separator = ',';
- }
- else
- separator = ',';
- }
- }
- /* Check whether opcode A's mask is more specific than that of B. */
- static int
- opcode_mask_more_specific (const struct s390_opcode *a,
- const struct s390_opcode *b)
- {
- return (((int) a->mask[0] + a->mask[1] + a->mask[2]
- + a->mask[3] + a->mask[4] + a->mask[5])
- > ((int) b->mask[0] + b->mask[1] + b->mask[2]
- + b->mask[3] + b->mask[4] + b->mask[5]));
- }
- /* Print a S390 instruction. */
- int
- print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
- {
- bfd_byte buffer[6];
- const struct s390_opcode *opcode = NULL;
- unsigned int value;
- int status, opsize, bufsize;
- if (init_flag == 0)
- init_disasm (info);
- /* The output looks better if we put 6 bytes on a line. */
- info->bytes_per_line = 6;
- /* Every S390 instruction is max 6 bytes long. */
- memset (buffer, 0, 6);
- status = info->read_memory_func (memaddr, buffer, 6, info);
- if (status != 0)
- {
- for (bufsize = 0; bufsize < 6; bufsize++)
- if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0)
- break;
- if (bufsize <= 0)
- {
- info->memory_error_func (status, memaddr, info);
- return -1;
- }
- opsize = s390_insn_length (buffer);
- status = opsize > bufsize;
- }
- else
- {
- bufsize = 6;
- opsize = s390_insn_length (buffer);
- }
- if (status == 0)
- {
- const struct s390_opcode *op;
- /* Find the "best match" in the opcode table. */
- for (op = s390_opcodes + opc_index[buffer[0]];
- op != s390_opcodes + s390_num_opcodes
- && op->opcode[0] == buffer[0];
- op++)
- {
- if ((op->modes & current_arch_mask)
- && s390_insn_matches_opcode (buffer, op)
- && (opcode == NULL
- || opcode_mask_more_specific (op, opcode)))
- opcode = op;
- }
- }
- if (opcode != NULL)
- {
- /* The instruction is valid. Print it and return its size. */
- s390_print_insn_with_opcode (memaddr, info, buffer, opcode);
- return opsize;
- }
- /* Fall back to hex print. */
- if (bufsize >= 4)
- {
- value = (unsigned int) buffer[0];
- value = (value << 8) + (unsigned int) buffer[1];
- value = (value << 8) + (unsigned int) buffer[2];
- value = (value << 8) + (unsigned int) buffer[3];
- info->fprintf_func (info->stream, ".long\t0x%08x", value);
- return 4;
- }
- else if (bufsize >= 2)
- {
- value = (unsigned int) buffer[0];
- value = (value << 8) + (unsigned int) buffer[1];
- info->fprintf_func (info->stream, ".short\t0x%04x", value);
- return 2;
- }
- else
- {
- value = (unsigned int) buffer[0];
- info->fprintf_func (info->stream, ".byte\t0x%02x", value);
- return 1;
- }
- }
- void
- print_s390_disassembler_options (FILE *stream)
- {
- fprintf (stream, _("\n\
- The following S/390 specific disassembler options are supported for use\n\
- with the -M switch (multiple options should be separated by commas):\n"));
- fprintf (stream, _(" esa Disassemble in ESA architecture mode\n"));
- fprintf (stream, _(" zarch Disassemble in z/Architecture mode\n"));
- }
|