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 /* Omit index register 0. */
208 if ((flags
& S390_OPERAND_INDEX
) && val
.u
== 0)
210 /* Omit base register 0, if no or omitted index register 0. */
211 if ((flags
& S390_OPERAND_BASE
) && val
.u
== 0 && separator
== '(')
217 /* For instructions with a last optional operand don't print it
219 if ((opcode
->flags
& (S390_INSTR_FLAG_OPTPARM
| S390_INSTR_FLAG_OPTPARM2
))
224 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM2
)
225 && val
.u
== 0 && opindex
[1] != 0 && opindex
[2] == 0)
227 union operand_value next_op_val
=
228 s390_extract_operand (buffer
, s390_operands
+ opindex
[1]);
229 if (next_op_val
.u
== 0)
233 if (flags
& S390_OPERAND_GPR
)
235 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
237 if ((flags
& (S390_OPERAND_BASE
| S390_OPERAND_INDEX
))
239 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
242 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
245 else if (flags
& S390_OPERAND_FPR
)
247 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
249 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
252 else if (flags
& S390_OPERAND_VR
)
254 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
256 if ((flags
& S390_OPERAND_INDEX
) && val
.u
== 0)
257 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
260 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
263 else if (flags
& S390_OPERAND_AR
)
265 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
267 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
270 else if (flags
& S390_OPERAND_CR
)
272 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
274 info
->fprintf_styled_func (info
->stream
, dis_style_register
,
277 else if (flags
& S390_OPERAND_PCREL
)
279 bfd_vma target
= memaddr
+ val
.i
+ val
.i
;
281 /* Provide info for jump visualization. May be evaluated by p_a_f(). */
282 info
->target
= target
;
284 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
286 info
->print_address_func (target
, info
);
288 else if (flags
& S390_OPERAND_SIGNED
)
290 enum disassembler_style style
;
292 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
294 style
= ((flags
& S390_OPERAND_DISP
)
295 ? dis_style_address_offset
: dis_style_immediate
);
296 info
->fprintf_styled_func (info
->stream
, style
, "%i", val
.i
);
300 enum disassembler_style style
;
302 if (flags
& S390_OPERAND_OR1
)
304 if (flags
& S390_OPERAND_OR2
)
306 if (flags
& S390_OPERAND_OR8
)
309 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM
)
313 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
315 style
= ((flags
& S390_OPERAND_DISP
)
316 ? dis_style_address_offset
: dis_style_immediate
);
317 info
->fprintf_styled_func (info
->stream
, style
, "%u", val
.u
);
320 if (flags
& S390_OPERAND_DISP
)
322 else if (flags
& S390_OPERAND_BASE
)
324 info
->fprintf_styled_func (info
->stream
, dis_style_text
, ")");
331 /* Optional: instruction name. */
332 if (option_print_insn_desc
&& opcode
->description
333 && opcode
->description
[0] != '\0')
334 info
->fprintf_styled_func (info
->stream
, dis_style_comment_start
, "\t# %s",
335 opcode
->description
);
338 /* Check whether opcode A's mask is more specific than that of B. */
341 opcode_mask_more_specific (const struct s390_opcode
*a
,
342 const struct s390_opcode
*b
)
344 return (((int) a
->mask
[0] + a
->mask
[1] + a
->mask
[2]
345 + a
->mask
[3] + a
->mask
[4] + a
->mask
[5])
346 > ((int) b
->mask
[0] + b
->mask
[1] + b
->mask
[2]
347 + b
->mask
[3] + b
->mask
[4] + b
->mask
[5]));
350 /* Print a S390 instruction. */
353 print_insn_s390 (bfd_vma memaddr
, struct disassemble_info
*info
)
356 const struct s390_opcode
*opcode
= NULL
;
358 int status
, opsize
, bufsize
, bytes_to_dump
, i
;
360 /* The output looks better if we put 6 bytes on a line. */
361 info
->bytes_per_line
= 6;
363 /* Set some defaults for the insn info. */
364 info
->insn_info_valid
= 0;
365 info
->branch_delay_insns
= 0;
367 info
->insn_type
= dis_nonbranch
;
371 /* Every S390 instruction is max 6 bytes long. */
372 memset (buffer
, 0, 6);
373 status
= info
->read_memory_func (memaddr
, buffer
, 6, info
);
376 for (bufsize
= 0; bufsize
< 6; bufsize
++)
377 if (info
->read_memory_func (memaddr
, buffer
, bufsize
+ 1, info
) != 0)
381 info
->memory_error_func (status
, memaddr
, info
);
384 opsize
= s390_insn_length (buffer
);
385 status
= opsize
> bufsize
;
390 opsize
= s390_insn_length (buffer
);
395 const struct s390_opcode
*op
;
397 /* Find the "best match" in the opcode table. */
398 for (op
= s390_opcodes
+ opc_index
[buffer
[0]];
399 op
!= s390_opcodes
+ s390_num_opcodes
400 && op
->opcode
[0] == buffer
[0];
403 if ((op
->modes
& current_arch_mask
)
404 && s390_insn_matches_opcode (buffer
, op
)
406 || opcode_mask_more_specific (op
, opcode
)))
412 /* Provide info for jump visualization. Must be done before print. */
413 switch (opcode
->flags
& S390_INSTR_FLAG_CLASS_MASK
)
415 case S390_INSTR_FLAGS_CLASS_JUMP
:
416 info
->insn_type
= dis_branch
;
418 case S390_INSTR_FLAGS_CLASS_CONDJUMP
:
419 info
->insn_type
= dis_condbranch
;
421 case S390_INSTR_FLAGS_CLASS_JUMPSR
:
422 info
->insn_type
= dis_jsr
;
425 info
->insn_type
= dis_nonbranch
;
427 info
->insn_info_valid
= 1;
429 /* The instruction is valid. Print it and return its size. */
430 s390_print_insn_with_opcode (memaddr
, info
, buffer
, opcode
);
435 /* For code sections it makes sense to skip unknown instructions
436 according to their length bits. */
438 && option_use_insn_len_bits_p
439 && info
->section
!= NULL
440 && (info
->section
->flags
& SEC_CODE
))
441 bytes_to_dump
= opsize
;
443 /* By default unknown instructions are printed as .long's/.short'
444 depending on how many bytes are available. */
445 bytes_to_dump
= bufsize
>= 4 ? 4 : bufsize
;
447 if (bytes_to_dump
== 0)
450 info
->insn_type
= dis_noninsn
;
451 info
->insn_info_valid
= 1;
453 /* Fall back to hex print. */
454 switch (bytes_to_dump
)
457 value
= (unsigned int) buffer
[0];
458 value
= (value
<< 8) + (unsigned int) buffer
[1];
459 value
= (value
<< 8) + (unsigned int) buffer
[2];
460 value
= (value
<< 8) + (unsigned int) buffer
[3];
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 value
= (unsigned int) buffer
[0];
470 value
= (value
<< 8) + (unsigned int) buffer
[1];
471 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
473 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
475 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
479 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
481 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
483 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
484 "0x%02x", (unsigned int) buffer
[0]);
485 for (i
= 1; i
< bytes_to_dump
; i
++)
486 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
487 "0x%02x", (unsigned int) buffer
[i
]);
488 return bytes_to_dump
;
493 const disasm_options_and_args_t
*
494 disassembler_options_s390 (void)
496 static disasm_options_and_args_t
*opts_and_args
;
498 if (opts_and_args
== NULL
)
500 size_t i
, num_options
= ARRAY_SIZE (options
);
501 disasm_options_t
*opts
;
503 opts_and_args
= XNEW (disasm_options_and_args_t
);
504 opts_and_args
->args
= NULL
;
506 opts
= &opts_and_args
->options
;
507 opts
->name
= XNEWVEC (const char *, num_options
+ 1);
508 opts
->description
= XNEWVEC (const char *, num_options
+ 1);
510 for (i
= 0; i
< num_options
; i
++)
512 opts
->name
[i
] = options
[i
].name
;
513 opts
->description
[i
] = _(options
[i
].description
);
515 /* The array we return must be NULL terminated. */
516 opts
->name
[i
] = NULL
;
517 opts
->description
[i
] = NULL
;
520 return opts_and_args
;
524 print_s390_disassembler_options (FILE *stream
)
526 unsigned int i
, max_len
= 0;
527 fprintf (stream
, _("\n\
528 The following S/390 specific disassembler options are supported for use\n\
529 with the -M switch (multiple options should be separated by commas):\n"));
531 for (i
= 0; i
< ARRAY_SIZE (options
); i
++)
533 unsigned int len
= strlen (options
[i
].name
);
538 for (i
= 0, max_len
++; i
< ARRAY_SIZE (options
); i
++)
539 fprintf (stream
, " %s%*c %s\n",
541 (int)(max_len
- strlen (options
[i
].name
)), ' ',
542 _(options
[i
].description
));