binutils-gdb/opcodes/s390-dis.c
Maciej W. Rozycki 471b9d1507 GDB PR tdep/8282: MIPS: Wire in `set disassembler-options'
Implement MIPS target support for passing options to the disassembler,
complementing commit 65b48a8140 ("GDB: Add support for the new
set/show disassembler-options commands.").

This includes options that expect an argument, so adjust the generic
code and data structures used so as to handle such options.  So as to
give backends syntax flexibility no specific delimiter has been defined
to separate options from their respective arguments, so it has to be
included as the last character of the option name.  Completion code
however has not been adjusted and consequently option arguments cannot
be completed at this time.

Also the MIPS target has non-empty defaults for the options, so that ABI
names for the general-purpose registers respect our `set mips abi ...'
setting rather than always being determined from the ELF headers of the
binary file selected.  Handle these defaults as implicit options, never
shown to the user and always prepended to the user-specified options, so
that the latters can override the defaults.

The resulting output for the MIPS target is as follows:

(gdb) show disassembler-options
The current disassembler options are ''

The following disassembler options are supported for use with the
'set disassembler-options <option>[,<option>...]' command:

  no-aliases      Use canonical instruction forms.

  msa             Recognize MSA instructions.

  virt            Recognize the virtualization ASE instructions.

  xpa             Recognize the eXtended Physical Address (XPA) ASE
                  instructions.

  ginv            Recognize the Global INValidate (GINV) ASE instructions.

  gpr-names=ABI   Print GPR names according to specified ABI.
                  Default: based on binary being disassembled.

  fpr-names=ABI   Print FPR names according to specified ABI.
                  Default: numeric.

  cp0-names=ARCH  Print CP0 register names according to specified architecture.
                  Default: based on binary being disassembled.

  hwr-names=ARCH  Print HWR names according to specified architecture.
                  Default: based on binary being disassembled.

  reg-names=ABI   Print GPR and FPR names according to specified ABI.

  reg-names=ARCH  Print CP0 register and HWR names according to specified
                  architecture.

  For the options above, the following values are supported for "ABI":
    numeric 32 n32 64

  For the options above, the following values are supported for "ARCH":
    numeric r3000 r3900 r4000 r4010 vr4100 vr4111 vr4120 r4300 r4400 r4600
    r4650 r5000 vr5400 vr5500 r5900 r6000 rm7000 rm9000 r8000 r10000 r12000
    r14000 r16000 mips5 mips32 mips32r2 mips32r3 mips32r5 mips32r6 mips64
    mips64r2 mips64r3 mips64r5 mips64r6 interaptiv-mr2 sb1 loongson2e
    loongson2f loongson3a octeon octeon+ octeon2 octeon3 xlr xlp
(gdb)

which corresponds to what `objdump --help' used to print for the MIPS
target, with minor formatting changes, most notably option argument
lists being wrapped, but also the amount of white space separating
options from the respective descriptions.  The relevant part the new
code is now also used by `objdump --help', which means these formatting
changes apply to both outputs, except for argument list wrapping, which
is GDB-specific.

This also adds a separating new line between the heading and option
lists where descriptions are provided, hence:

(gdb) set architecture s390:31-bit
(gdb) show disassembler-options
The current disassembler options are ''

The following disassembler options are supported for use with the
'set disassembler-options <option>[,<option>...]' command:

  esa         Disassemble in ESA architecture mode
  zarch       Disassemble in z/Architecture mode
  insnlength  Print unknown instructions according to length from first two bits
(gdb)

but:

(gdb) set architecture powerpc:common
(gdb) show disassembler-options
The current disassembler options are ''

The following disassembler options are supported for use with the
'set disassembler-options <option>[,<option>...]' command:
  403, 405, 440, 464, 476, 601, 603, 604, 620, 7400, 7410, 7450, 7455, 750cl,
  821, 850, 860, a2, altivec, any, booke, booke32, cell, com, e200z4, e300,
  e500, e500mc, e500mc64, e5500, e6500, e500x2, efs, efs2, power4, power5,
  power6, power7, power8, power9, ppc, ppc32, 32, ppc64, 64, ppc64bridge,
  ppcps, pwr, pwr2, pwr4, pwr5, pwr5x, pwr6, pwr7, pwr8, pwr9, pwrx, raw, spe,
  spe2, titan, vle, vsx
(gdb)

Existing affected target backends have been adjusted accordingly.

This has been verified manually with:

(gdb) set architecture arm
(gdb) set architecture powerpc:common
(gdb) set architecture s390:31-bit

to cause no issues with the `show disassembler-options' and `set
disassembler-options' commands.  A test case for the MIPS target has
also been provided, covering the default settings with ABI overrides as
well as disassembler option overrides.

2018-07-02  Maciej W. Rozycki  <macro@mips.com>
            Simon Marchi  <simon.marchi@polymtl.ca>

	include/
	PR tdep/8282
	* dis-asm.h (disasm_option_arg_t): New typedef.
	(disasm_options_and_args_t): Likewise.
	(disasm_options_t): Add `arg' member, document members.
	(disassembler_options_mips): New prototype.
	(disassembler_options_arm, disassembler_options_powerpc)
	(disassembler_options_s390): Update prototypes.

	opcodes/
	PR tdep/8282
	* mips-dis.c (mips_option_arg_t): New enumeration.
	(mips_options): New variable.
	(disassembler_options_mips): New function.
	(print_mips_disassembler_options): Reimplement in terms of
	`disassembler_options_mips'.
	* arm-dis.c (disassembler_options_arm): Adapt to using the
	`disasm_options_and_args_t' structure.
	* ppc-dis.c (disassembler_options_powerpc): Likewise.
	* s390-dis.c (disassembler_options_s390): Likewise.

	gdb/
	PR tdep/8282
	* disasm.h (gdb_disassembler): Add
	`m_disassembler_options_holder'. member
	* disasm.c (get_all_disassembler_options): New function.
	(gdb_disassembler::gdb_disassembler): Use it.
	(gdb_buffered_insn_length_init_dis): Likewise.
	(gdb_buffered_insn_length): Adjust accordingly.
	(set_disassembler_options): Handle options with arguments.
	(show_disassembler_options_sfunc): Likewise.  Add a leading new
	line if showing options with descriptions.
	(disassembler_options_completer): Adapt to using the
	`disasm_options_and_args_t' structure.
	* mips-tdep.c (mips_disassembler_options): New variable.
	(mips_disassembler_options_o32): Likewise.
	(mips_disassembler_options_n32): Likewise.
	(mips_disassembler_options_n64): Likewise.
	(gdb_print_insn_mips): Don't set `disassembler_options'.
	(gdb_print_insn_mips_n32, gdb_print_insn_mips_n64): Remove
	functions.
	(mips_gdbarch_init): Always set `gdbarch_print_insn' to
	`gdb_print_insn_mips'.  Set `gdbarch_disassembler_options',
	`gdbarch_disassembler_options_implicit' and
	`gdbarch_valid_disassembler_options'.
	* arm-tdep.c (_initialize_arm_tdep): Adapt to using the
	`disasm_options_and_args_t' structure.
	* gdbarch.sh (disassembler_options_implicit): New `gdbarch'
	method.
	(valid_disassembler_options): Switch from `disasm_options_t' to
	the `disasm_options_and_args_t' structure.
	* NEWS: Document `set disassembler-options' support for the MIPS
	target.
	* gdbarch.h: Regenerate.
	* gdbarch.c: Regenerate.

	gdb/doc/
	PR tdep/8282
	* gdb.texinfo (Source and Machine Code): Document `set
	disassembler-options' support for the MIPS target.

	gdb/testsuite/
	PR tdep/8282
	* gdb.arch/mips-disassembler-options.exp: New test.
	* gdb.arch/mips-disassembler-options.s: New test source.
2018-07-02 23:57:21 +01:00

433 lines
12 KiB
C

/* s390-dis.c -- Disassemble S390 instructions
Copyright (C) 2000-2018 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 "disassemble.h"
#include "opintl.h"
#include "opcode/s390.h"
#include "libiberty.h"
static int opc_index[256];
static int current_arch_mask = 0;
static int option_use_insn_len_bits_p = 0;
typedef struct
{
const char *name;
const char *description;
} s390_options_t;
static const s390_options_t options[] =
{
{ "esa" , N_("Disassemble in ESA architecture mode") },
{ "zarch", N_("Disassemble in z/Architecture mode") },
{ "insnlength", N_("Print unknown instructions according to "
"length from first two bits") }
};
/* Set up index table for first opcode byte. */
void
disassemble_init_s390 (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;
current_arch_mask = 1 << S390_OPCODE_ZARCH;
option_use_insn_len_bits_p = 0;
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 if (CONST_STRNEQ (p, "insnlength"))
option_use_insn_len_bits_p = 1;
else
/* xgettext:c-format */
opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p);
p = strchr (p, ',');
if (p != NULL)
p++;
}
}
/* 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 | S390_INSTR_FLAG_OPTPARM2))
&& val.u == 0
&& opindex[1] == 0)
break;
if ((opcode->flags & S390_INSTR_FLAG_OPTPARM2)
&& val.u == 0 && opindex[1] != 0 && opindex[2] == 0)
{
union operand_value next_op_val =
s390_extract_operand (buffer, s390_operands + opindex[1]);
if (next_op_val.u == 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, bytes_to_dump, i;
/* 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;
}
}
/* For code sections it makes sense to skip unknown instructions
according to their length bits. */
if (status == 0
&& option_use_insn_len_bits_p
&& info->section != NULL
&& (info->section->flags & SEC_CODE))
bytes_to_dump = opsize;
else
/* By default unknown instructions are printed as .long's/.short'
depending on how many bytes are available. */
bytes_to_dump = bufsize >= 4 ? 4 : bufsize;
if (bytes_to_dump == 0)
return 0;
/* Fall back to hex print. */
switch (bytes_to_dump)
{
case 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;
case 2:
value = (unsigned int) buffer[0];
value = (value << 8) + (unsigned int) buffer[1];
info->fprintf_func (info->stream, ".short\t0x%04x", value);
return 2;
default:
info->fprintf_func (info->stream, ".byte\t0x%02x",
(unsigned int) buffer[0]);
for (i = 1; i < bytes_to_dump; i++)
info->fprintf_func (info->stream, ",0x%02x",
(unsigned int) buffer[i]);
return bytes_to_dump;
}
return 0;
}
const disasm_options_and_args_t *
disassembler_options_s390 (void)
{
static disasm_options_and_args_t *opts_and_args;
if (opts_and_args == NULL)
{
size_t i, num_options = ARRAY_SIZE (options);
disasm_options_t *opts;
opts_and_args = XNEW (disasm_options_and_args_t);
opts_and_args->args = NULL;
opts = &opts_and_args->options;
opts->name = XNEWVEC (const char *, num_options + 1);
opts->description = XNEWVEC (const char *, num_options + 1);
opts->arg = NULL;
for (i = 0; i < num_options; i++)
{
opts->name[i] = options[i].name;
opts->description[i] = _(options[i].description);
}
/* The array we return must be NULL terminated. */
opts->name[i] = NULL;
opts->description[i] = NULL;
}
return opts_and_args;
}
void
print_s390_disassembler_options (FILE *stream)
{
unsigned int i, max_len = 0;
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"));
for (i = 0; i < ARRAY_SIZE (options); i++)
{
unsigned int len = strlen (options[i].name);
if (max_len < len)
max_len = len;
}
for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
fprintf (stream, " %s%*c %s\n",
options[i].name,
(int)(max_len - strlen (options[i].name)), ' ',
_(options[i].description));
}