1 /* s390-dis.c -- Disassemble S390 instructions
2 Copyright (C) 2000-2024 Free Software Foundation, Inc.
3 Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
5 This file is part of the GNU opcodes library.
7 This library is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 It is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this file; see the file COPYING. If not, write to the
19 Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
20 MA 02110-1301, USA. */
25 #include "disassemble.h"
27 #include "opcode/s390.h"
28 #include "libiberty.h"
31 static int opc_index
[256];
32 static int current_arch_mask
= 0;
33 static int option_use_insn_len_bits_p
= 0;
34 static int option_print_insn_desc
= 0;
39 const char *description
;
42 static const s390_options_t options
[] =
44 { "esa" , N_("Disassemble in ESA architecture mode") },
45 /* TRANSLATORS: Please do not translate 'z/Architecture' as this is a technical name. */
46 { "zarch", N_("Disassemble in z/Architecture mode") },
47 { "insnlength", N_("Print unknown instructions according to "
48 "length from first two bits") },
49 { "insndesc", N_("Print instruction description as comment") },
52 /* Set up index table for first opcode byte. */
55 disassemble_init_s390 (struct disassemble_info
*info
)
60 memset (opc_index
, 0, sizeof (opc_index
));
62 /* Reverse order, such that each opc_index ends up pointing to the
63 first matching entry instead of the last. */
64 for (i
= s390_num_opcodes
; i
--; )
65 opc_index
[s390_opcodes
[i
].opcode
[0]] = i
;
67 current_arch_mask
= 1 << S390_OPCODE_ZARCH
;
68 option_use_insn_len_bits_p
= 0;
69 option_print_insn_desc
= 0;
71 for (p
= info
->disassembler_options
; p
!= NULL
; )
73 if (startswith (p
, "esa"))
74 current_arch_mask
= 1 << S390_OPCODE_ESA
;
75 else if (startswith (p
, "zarch"))
76 current_arch_mask
= 1 << S390_OPCODE_ZARCH
;
77 else if (startswith (p
, "insnlength"))
78 option_use_insn_len_bits_p
= 1;
79 else if (startswith (p
, "insndesc"))
80 option_print_insn_desc
= 1;
82 /* xgettext:c-format */
83 opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p
);
91 /* Derive the length of an instruction from its first byte. */
94 s390_insn_length (const bfd_byte
*buffer
)
96 /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6. */
97 return ((buffer
[0] >> 6) + 3) & ~1U;
100 /* Match the instruction in BUFFER against the given OPCODE, excluding
104 s390_insn_matches_opcode (const bfd_byte
*buffer
,
105 const struct s390_opcode
*opcode
)
107 return (buffer
[1] & opcode
->mask
[1]) == opcode
->opcode
[1]
108 && (buffer
[2] & opcode
->mask
[2]) == opcode
->opcode
[2]
109 && (buffer
[3] & opcode
->mask
[3]) == opcode
->opcode
[3]
110 && (buffer
[4] & opcode
->mask
[4]) == opcode
->opcode
[4]
111 && (buffer
[5] & opcode
->mask
[5]) == opcode
->opcode
[5];
120 /* Extracts an operand value from an instruction. */
121 /* We do not perform the shift operation for larl-type address
122 operands here since that would lead to an overflow of the 32 bit
123 integer value. Instead the shift operation is done when printing
126 static inline union operand_value
127 s390_extract_operand (const bfd_byte
*insn
,
128 const struct s390_operand
*operand
)
130 union operand_value ret
;
133 const bfd_byte
*orig_insn
= insn
;
135 /* Extract fragments of the operand byte for byte. */
136 insn
+= operand
->shift
/ 8;
137 bits
= (operand
->shift
& 7) + operand
->bits
;
142 val
|= (unsigned int) *insn
++;
147 val
&= ((1U << (operand
->bits
- 1)) << 1) - 1;
149 /* Check for special long displacement case. */
150 if (operand
->bits
== 20 && operand
->shift
== 20)
151 val
= (val
& 0xff) << 12 | (val
& 0xfff00) >> 8;
153 /* Sign extend value if the operand is signed or pc relative. Avoid
154 integer overflows. */
155 if (operand
->flags
& (S390_OPERAND_SIGNED
| S390_OPERAND_PCREL
))
157 unsigned int m
= 1U << (operand
->bits
- 1);
160 ret
.i
= (int) (val
- m
) - 1 - (int) (m
- 1U);
164 else if (operand
->flags
& S390_OPERAND_LENGTH
)
165 /* Length x in an instruction has real length x + 1. */
168 else if (operand
->flags
& S390_OPERAND_VR
)
170 /* Extract the extra bits for a vector register operand stored
172 unsigned vr
= operand
->shift
== 32 ? 3
173 : (unsigned) operand
->shift
/ 4 - 2;
175 ret
.u
= val
| ((orig_insn
[4] & (1 << (3 - vr
))) << (vr
+ 1));
183 /* Print the S390 instruction in BUFFER, assuming that it matches the
187 s390_print_insn_with_opcode (bfd_vma memaddr
,
188 struct disassemble_info
*info
,
189 const bfd_byte
*buffer
,
190 const struct s390_opcode
*opcode
)
192 const unsigned char *opindex
;
196 info
->fprintf_styled_func (info
->stream
, dis_style_mnemonic
,
201 for (opindex
= opcode
->operands
; *opindex
!= 0; opindex
++)
203 const struct s390_operand
*operand
= s390_operands
+ *opindex
;
204 union operand_value val
= s390_extract_operand (buffer
, operand
);
205 unsigned long flags
= operand
->flags
;
207 if ((flags
& S390_OPERAND_INDEX
) && val
.u
== 0)
209 if ((flags
& S390_OPERAND_BASE
) &&
210 val
.u
== 0 && separator
== '(')
216 /* For instructions with a last optional operand don't print it
218 if ((opcode
->flags
& (S390_INSTR_FLAG_OPTPARM
| S390_INSTR_FLAG_OPTPARM2
))
223 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM2
)
224 && val
.u
== 0 && opindex
[1] != 0 && opindex
[2] == 0)
226 union operand_value next_op_val
=
227 s390_extract_operand (buffer
, s390_operands
+ opindex
[1]);
228 if (next_op_val
.u
== 0)
232 if (flags
& S390_OPERAND_GPR
)
234 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
236 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
239 else if (flags
& S390_OPERAND_FPR
)
241 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
243 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
246 else if (flags
& S390_OPERAND_VR
)
248 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
250 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
253 else if (flags
& S390_OPERAND_AR
)
255 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
257 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
260 else if (flags
& S390_OPERAND_CR
)
262 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
264 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
267 else if (flags
& S390_OPERAND_PCREL
)
269 bfd_vma target
= memaddr
+ val
.i
+ val
.i
;
271 /* Provide info for jump visualization. May be evaluated by p_a_f(). */
272 info
->target
= target
;
274 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
276 info
->print_address_func (target
, info
);
278 else if (flags
& S390_OPERAND_SIGNED
)
280 enum disassembler_style style
;
282 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
284 style
= ((flags
& S390_OPERAND_DISP
)
285 ? dis_style_address_offset
: dis_style_immediate
);
286 info
->fprintf_styled_func (info
->stream
, style
, "%i", val
.i
);
290 enum disassembler_style style
;
292 if (flags
& S390_OPERAND_OR1
)
294 if (flags
& S390_OPERAND_OR2
)
296 if (flags
& S390_OPERAND_OR8
)
299 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM
)
303 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
305 style
= ((flags
& S390_OPERAND_DISP
)
306 ? dis_style_address_offset
: dis_style_immediate
);
307 info
->fprintf_styled_func (info
->stream
, style
, "%u", val
.u
);
310 if (flags
& S390_OPERAND_DISP
)
312 else if (flags
& S390_OPERAND_BASE
)
314 info
->fprintf_styled_func (info
->stream
, dis_style_text
, ")");
321 /* Optional: instruction name. */
322 if (option_print_insn_desc
&& opcode
->description
323 && opcode
->description
[0] != '\0')
324 info
->fprintf_styled_func (info
->stream
, dis_style_comment_start
, "\t# %s",
325 opcode
->description
);
328 /* Check whether opcode A's mask is more specific than that of B. */
331 opcode_mask_more_specific (const struct s390_opcode
*a
,
332 const struct s390_opcode
*b
)
334 return (((int) a
->mask
[0] + a
->mask
[1] + a
->mask
[2]
335 + a
->mask
[3] + a
->mask
[4] + a
->mask
[5])
336 > ((int) b
->mask
[0] + b
->mask
[1] + b
->mask
[2]
337 + b
->mask
[3] + b
->mask
[4] + b
->mask
[5]));
340 /* Print a S390 instruction. */
343 print_insn_s390 (bfd_vma memaddr
, struct disassemble_info
*info
)
346 const struct s390_opcode
*opcode
= NULL
;
348 int status
, opsize
, bufsize
, bytes_to_dump
, i
;
350 /* The output looks better if we put 6 bytes on a line. */
351 info
->bytes_per_line
= 6;
353 /* Set some defaults for the insn info. */
354 info
->insn_info_valid
= 0;
355 info
->branch_delay_insns
= 0;
357 info
->insn_type
= dis_nonbranch
;
361 /* Every S390 instruction is max 6 bytes long. */
362 memset (buffer
, 0, 6);
363 status
= info
->read_memory_func (memaddr
, buffer
, 6, info
);
366 for (bufsize
= 0; bufsize
< 6; bufsize
++)
367 if (info
->read_memory_func (memaddr
, buffer
, bufsize
+ 1, info
) != 0)
371 info
->memory_error_func (status
, memaddr
, info
);
374 opsize
= s390_insn_length (buffer
);
375 status
= opsize
> bufsize
;
380 opsize
= s390_insn_length (buffer
);
385 const struct s390_opcode
*op
;
387 /* Find the "best match" in the opcode table. */
388 for (op
= s390_opcodes
+ opc_index
[buffer
[0]];
389 op
!= s390_opcodes
+ s390_num_opcodes
390 && op
->opcode
[0] == buffer
[0];
393 if ((op
->modes
& current_arch_mask
)
394 && s390_insn_matches_opcode (buffer
, op
)
396 || opcode_mask_more_specific (op
, opcode
)))
402 /* Provide info for jump visualization. Must be done before print. */
403 switch (opcode
->flags
& S390_INSTR_FLAG_CLASS_MASK
)
405 case S390_INSTR_FLAGS_CLASS_JUMP
:
406 info
->insn_type
= dis_branch
;
408 case S390_INSTR_FLAGS_CLASS_CONDJUMP
:
409 info
->insn_type
= dis_condbranch
;
411 case S390_INSTR_FLAGS_CLASS_JUMPSR
:
412 info
->insn_type
= dis_jsr
;
415 info
->insn_type
= dis_nonbranch
;
417 info
->insn_info_valid
= 1;
419 /* The instruction is valid. Print it and return its size. */
420 s390_print_insn_with_opcode (memaddr
, info
, buffer
, opcode
);
425 /* For code sections it makes sense to skip unknown instructions
426 according to their length bits. */
428 && option_use_insn_len_bits_p
429 && info
->section
!= NULL
430 && (info
->section
->flags
& SEC_CODE
))
431 bytes_to_dump
= opsize
;
433 /* By default unknown instructions are printed as .long's/.short'
434 depending on how many bytes are available. */
435 bytes_to_dump
= bufsize
>= 4 ? 4 : bufsize
;
437 if (bytes_to_dump
== 0)
440 info
->insn_type
= dis_noninsn
;
441 info
->insn_info_valid
= 1;
443 /* Fall back to hex print. */
444 switch (bytes_to_dump
)
447 value
= (unsigned int) buffer
[0];
448 value
= (value
<< 8) + (unsigned int) buffer
[1];
449 value
= (value
<< 8) + (unsigned int) buffer
[2];
450 value
= (value
<< 8) + (unsigned int) buffer
[3];
451 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
453 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
455 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
459 value
= (unsigned int) buffer
[0];
460 value
= (value
<< 8) + (unsigned int) buffer
[1];
461 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
463 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
465 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
469 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
471 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
473 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
474 "0x%02x", (unsigned int) buffer
[0]);
475 for (i
= 1; i
< bytes_to_dump
; i
++)
476 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
477 "0x%02x", (unsigned int) buffer
[i
]);
478 return bytes_to_dump
;
483 const disasm_options_and_args_t
*
484 disassembler_options_s390 (void)
486 static disasm_options_and_args_t
*opts_and_args
;
488 if (opts_and_args
== NULL
)
490 size_t i
, num_options
= ARRAY_SIZE (options
);
491 disasm_options_t
*opts
;
493 opts_and_args
= XNEW (disasm_options_and_args_t
);
494 opts_and_args
->args
= NULL
;
496 opts
= &opts_and_args
->options
;
497 opts
->name
= XNEWVEC (const char *, num_options
+ 1);
498 opts
->description
= XNEWVEC (const char *, num_options
+ 1);
500 for (i
= 0; i
< num_options
; i
++)
502 opts
->name
[i
] = options
[i
].name
;
503 opts
->description
[i
] = _(options
[i
].description
);
505 /* The array we return must be NULL terminated. */
506 opts
->name
[i
] = NULL
;
507 opts
->description
[i
] = NULL
;
510 return opts_and_args
;
514 print_s390_disassembler_options (FILE *stream
)
516 unsigned int i
, max_len
= 0;
517 fprintf (stream
, _("\n\
518 The following S/390 specific disassembler options are supported for use\n\
519 with the -M switch (multiple options should be separated by commas):\n"));
521 for (i
= 0; i
< ARRAY_SIZE (options
); i
++)
523 unsigned int len
= strlen (options
[i
].name
);
528 for (i
= 0, max_len
++; i
< ARRAY_SIZE (options
); i
++)
529 fprintf (stream
, " %s%*c %s\n",
531 (int)(max_len
- strlen (options
[i
].name
)), ' ',
532 _(options
[i
].description
));