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_LENGTH
))
304 union operand_value insn_opval
;
306 /* Mask any constant operand bits set in insn template. */
307 insn_opval
= s390_extract_operand (opcode
->opcode
, operand
);
308 val
.u
&= ~insn_opval
.u
;
311 if ((opcode
->flags
& S390_INSTR_FLAG_OPTPARM
)
315 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
317 style
= ((flags
& S390_OPERAND_DISP
)
318 ? dis_style_address_offset
: dis_style_immediate
);
319 info
->fprintf_styled_func (info
->stream
, style
, "%u", val
.u
);
322 if (flags
& S390_OPERAND_DISP
)
324 else if (flags
& S390_OPERAND_BASE
)
326 info
->fprintf_styled_func (info
->stream
, dis_style_text
, ")");
333 /* Optional: instruction name. */
334 if (option_print_insn_desc
&& opcode
->description
335 && opcode
->description
[0] != '\0')
336 info
->fprintf_styled_func (info
->stream
, dis_style_comment_start
, "\t# %s",
337 opcode
->description
);
340 /* Check whether opcode A's mask is more specific than that of B. */
343 opcode_mask_more_specific (const struct s390_opcode
*a
,
344 const struct s390_opcode
*b
)
346 return (((int) a
->mask
[0] + a
->mask
[1] + a
->mask
[2]
347 + a
->mask
[3] + a
->mask
[4] + a
->mask
[5])
348 > ((int) b
->mask
[0] + b
->mask
[1] + b
->mask
[2]
349 + b
->mask
[3] + b
->mask
[4] + b
->mask
[5]));
352 /* Print a S390 instruction. */
355 print_insn_s390 (bfd_vma memaddr
, struct disassemble_info
*info
)
358 const struct s390_opcode
*opcode
= NULL
;
360 int status
, opsize
, bufsize
, bytes_to_dump
, i
;
362 /* The output looks better if we put 6 bytes on a line. */
363 info
->bytes_per_line
= 6;
365 /* Set some defaults for the insn info. */
366 info
->insn_info_valid
= 0;
367 info
->branch_delay_insns
= 0;
369 info
->insn_type
= dis_nonbranch
;
373 /* Every S390 instruction is max 6 bytes long. */
374 memset (buffer
, 0, 6);
375 status
= info
->read_memory_func (memaddr
, buffer
, 6, info
);
378 for (bufsize
= 0; bufsize
< 6; bufsize
++)
379 if (info
->read_memory_func (memaddr
, buffer
, bufsize
+ 1, info
) != 0)
383 info
->memory_error_func (status
, memaddr
, info
);
386 opsize
= s390_insn_length (buffer
);
387 status
= opsize
> bufsize
;
392 opsize
= s390_insn_length (buffer
);
397 const struct s390_opcode
*op
;
399 /* Find the "best match" in the opcode table. */
400 for (op
= s390_opcodes
+ opc_index
[buffer
[0]];
401 op
!= s390_opcodes
+ s390_num_opcodes
402 && op
->opcode
[0] == buffer
[0];
405 if ((op
->modes
& current_arch_mask
)
406 && s390_insn_matches_opcode (buffer
, op
)
408 || opcode_mask_more_specific (op
, opcode
)))
414 /* Provide info for jump visualization. Must be done before print. */
415 switch (opcode
->flags
& S390_INSTR_FLAG_CLASS_MASK
)
417 case S390_INSTR_FLAGS_CLASS_JUMP
:
418 info
->insn_type
= dis_branch
;
420 case S390_INSTR_FLAGS_CLASS_CONDJUMP
:
421 info
->insn_type
= dis_condbranch
;
423 case S390_INSTR_FLAGS_CLASS_JUMPSR
:
424 info
->insn_type
= dis_jsr
;
427 info
->insn_type
= dis_nonbranch
;
429 info
->insn_info_valid
= 1;
431 /* The instruction is valid. Print it and return its size. */
432 s390_print_insn_with_opcode (memaddr
, info
, buffer
, opcode
);
437 /* For code sections it makes sense to skip unknown instructions
438 according to their length bits. */
440 && option_use_insn_len_bits_p
441 && info
->section
!= NULL
442 && (info
->section
->flags
& SEC_CODE
))
443 bytes_to_dump
= opsize
;
445 /* By default unknown instructions are printed as .long's/.short'
446 depending on how many bytes are available. */
447 bytes_to_dump
= bufsize
>= 4 ? 4 : bufsize
;
449 if (bytes_to_dump
== 0)
452 info
->insn_type
= dis_noninsn
;
453 info
->insn_info_valid
= 1;
455 /* Fall back to hex print. */
456 switch (bytes_to_dump
)
459 value
= (unsigned int) buffer
[0];
460 value
= (value
<< 8) + (unsigned int) buffer
[1];
461 value
= (value
<< 8) + (unsigned int) buffer
[2];
462 value
= (value
<< 8) + (unsigned int) buffer
[3];
463 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
465 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
467 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
471 value
= (unsigned int) buffer
[0];
472 value
= (value
<< 8) + (unsigned int) buffer
[1];
473 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
475 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
477 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
481 info
->fprintf_styled_func (info
->stream
, dis_style_assembler_directive
,
483 info
->fprintf_styled_func (info
->stream
, dis_style_text
,
485 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
486 "0x%02x", (unsigned int) buffer
[0]);
487 for (i
= 1; i
< bytes_to_dump
; i
++)
488 info
->fprintf_styled_func (info
->stream
, dis_style_immediate
,
489 "0x%02x", (unsigned int) buffer
[i
]);
490 return bytes_to_dump
;
495 const disasm_options_and_args_t
*
496 disassembler_options_s390 (void)
498 static disasm_options_and_args_t
*opts_and_args
;
500 if (opts_and_args
== NULL
)
502 size_t i
, num_options
= ARRAY_SIZE (options
);
503 disasm_options_t
*opts
;
505 opts_and_args
= XNEW (disasm_options_and_args_t
);
506 opts_and_args
->args
= NULL
;
508 opts
= &opts_and_args
->options
;
509 opts
->name
= XNEWVEC (const char *, num_options
+ 1);
510 opts
->description
= XNEWVEC (const char *, num_options
+ 1);
512 for (i
= 0; i
< num_options
; i
++)
514 opts
->name
[i
] = options
[i
].name
;
515 opts
->description
[i
] = _(options
[i
].description
);
517 /* The array we return must be NULL terminated. */
518 opts
->name
[i
] = NULL
;
519 opts
->description
[i
] = NULL
;
522 return opts_and_args
;
526 print_s390_disassembler_options (FILE *stream
)
528 unsigned int i
, max_len
= 0;
529 fprintf (stream
, _("\n\
530 The following S/390 specific disassembler options are supported for use\n\
531 with the -M switch (multiple options should be separated by commas):\n"));
533 for (i
= 0; i
< ARRAY_SIZE (options
); i
++)
535 unsigned int len
= strlen (options
[i
].name
);
540 for (i
= 0, max_len
++; i
< ARRAY_SIZE (options
); i
++)
541 fprintf (stream
, " %s%*c %s\n",
543 (int)(max_len
- strlen (options
[i
].name
)), ' ',
544 _(options
[i
].description
));