xref: /netbsd-src/external/gpl3/gdb/dist/opcodes/s390-dis.c (revision 10b62e3d76b9d7658b723a95b83b5ba8105c0be5)
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).
4 
5    This file is part of the GNU opcodes library.
6 
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)
10    any later version.
11 
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.
16 
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.  */
21 
22 #include "sysdep.h"
23 #include <stdio.h>
24 #include "ansidecl.h"
25 #include "disassemble.h"
26 #include "opintl.h"
27 #include "opcode/s390.h"
28 #include "libiberty.h"
29 #include "dis-asm.h"
30 
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;
35 
36 typedef struct
37 {
38   const char *name;
39   const char *description;
40 } s390_options_t;
41 
42 static const s390_options_t options[] =
43 {
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") },
50 };
51 
52 /* Set up index table for first opcode byte.  */
53 
54 void
55 disassemble_init_s390 (struct disassemble_info *info)
56 {
57   int i;
58   const char *p;
59 
60   memset (opc_index, 0, sizeof (opc_index));
61 
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;
66 
67   current_arch_mask = 1 << S390_OPCODE_ZARCH;
68   option_use_insn_len_bits_p = 0;
69   option_print_insn_desc = 0;
70 
71   for (p = info->disassembler_options; p != NULL; )
72     {
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;
81       else
82 	/* xgettext:c-format */
83 	opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p);
84 
85       p = strchr (p, ',');
86       if (p != NULL)
87 	p++;
88     }
89 }
90 
91 /* Derive the length of an instruction from its first byte.  */
92 
93 static inline int
94 s390_insn_length (const bfd_byte *buffer)
95 {
96   /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6.  */
97   return ((buffer[0] >> 6) + 3) & ~1U;
98 }
99 
100 /* Match the instruction in BUFFER against the given OPCODE, excluding
101    the first byte.  */
102 
103 static inline int
104 s390_insn_matches_opcode (const bfd_byte *buffer,
105 			  const struct s390_opcode *opcode)
106 {
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];
112 }
113 
114 union operand_value
115 {
116   int i;
117   unsigned int u;
118 };
119 
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
124    the operand.  */
125 
126 static inline union operand_value
127 s390_extract_operand (const bfd_byte *insn,
128 		      const struct s390_operand *operand)
129 {
130   union operand_value ret;
131   unsigned int val;
132   int bits;
133   const bfd_byte *orig_insn = insn;
134 
135   /* Extract fragments of the operand byte for byte.  */
136   insn += operand->shift / 8;
137   bits = (operand->shift & 7) + operand->bits;
138   val = 0;
139   do
140     {
141       val <<= 8;
142       val |= (unsigned int) *insn++;
143       bits -= 8;
144     }
145   while (bits > 0);
146   val >>= -bits;
147   val &= ((1U << (operand->bits - 1)) << 1) - 1;
148 
149   /* Check for special long displacement case.  */
150   if (operand->bits == 20 && operand->shift == 20)
151     val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
152 
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))
156     {
157       unsigned int m = 1U << (operand->bits - 1);
158 
159       if (val >= m)
160 	ret.i = (int) (val - m) - 1 - (int) (m - 1U);
161       else
162 	ret.i = (int) val;
163     }
164   else if (operand->flags & S390_OPERAND_LENGTH)
165     /* Length x in an instruction has real length x + 1.  */
166     ret.u = val + 1;
167 
168   else if (operand->flags & S390_OPERAND_VR)
169     {
170       /* Extract the extra bits for a vector register operand stored
171 	 in the RXB field.  */
172       unsigned vr = operand->shift == 32 ? 3
173 	: (unsigned) operand->shift / 4 - 2;
174 
175       ret.u = val | ((orig_insn[4] & (1 << (3 - vr))) << (vr + 1));
176     }
177   else
178     ret.u = val;
179 
180   return ret;
181 }
182 
183 /* Print the S390 instruction in BUFFER, assuming that it matches the
184    given OPCODE.  */
185 
186 static void
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)
191 {
192   const unsigned char *opindex;
193   char separator;
194 
195   /* Mnemonic.  */
196   info->fprintf_styled_func (info->stream, dis_style_mnemonic,
197 			     "%s", opcode->name);
198 
199   /* Operands.  */
200   separator = '\t';
201   for (opindex = opcode->operands; *opindex != 0; opindex++)
202     {
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;
206 
207       /* Omit index register 0.  */
208       if ((flags & S390_OPERAND_INDEX) && val.u == 0)
209 	continue;
210       /* Omit base register 0, if no or omitted index register 0.  */
211       if ((flags & S390_OPERAND_BASE) && val.u == 0 && separator == '(')
212 	{
213 	  separator = ',';
214 	  continue;
215 	}
216 
217       /* For instructions with a last optional operand don't print it
218 	 if zero.  */
219       if ((opcode->flags & (S390_INSTR_FLAG_OPTPARM | S390_INSTR_FLAG_OPTPARM2))
220 	  && val.u == 0
221 	  && opindex[1] == 0)
222 	break;
223 
224       if ((opcode->flags & S390_INSTR_FLAG_OPTPARM2)
225 	  && val.u == 0 && opindex[1] != 0 && opindex[2] == 0)
226 	{
227 	  union operand_value next_op_val =
228 	    s390_extract_operand (buffer, s390_operands + opindex[1]);
229 	  if (next_op_val.u == 0)
230 	    break;
231 	}
232 
233       if (flags & S390_OPERAND_GPR)
234 	{
235 	  info->fprintf_styled_func (info->stream, dis_style_text,
236 				     "%c", separator);
237 	  if ((flags & (S390_OPERAND_BASE | S390_OPERAND_INDEX))
238 	      && val.u == 0)
239 	    info->fprintf_styled_func (info->stream, dis_style_register,
240 				       "%u", val.u);
241 	  else
242 	    info->fprintf_styled_func (info->stream, dis_style_register,
243 				       "%%r%u", val.u);
244 	}
245       else if (flags & S390_OPERAND_FPR)
246 	{
247 	  info->fprintf_styled_func (info->stream, dis_style_text,
248 				     "%c", separator);
249 	  info->fprintf_styled_func (info->stream, dis_style_register,
250 				     "%%f%u", val.u);
251 	}
252       else if (flags & S390_OPERAND_VR)
253 	{
254 	  info->fprintf_styled_func (info->stream, dis_style_text,
255 				     "%c", separator);
256 	  if ((flags & S390_OPERAND_INDEX) && val.u == 0)
257 	    info->fprintf_styled_func (info->stream, dis_style_register,
258 				       "%u", val.u);
259 	  else
260 	    info->fprintf_styled_func (info->stream, dis_style_register,
261 				       "%%v%i", val.u);
262 	}
263       else if (flags & S390_OPERAND_AR)
264 	{
265 	  info->fprintf_styled_func (info->stream, dis_style_text,
266 				     "%c", separator);
267 	  info->fprintf_styled_func (info->stream, dis_style_register,
268 				     "%%a%u", val.u);
269 	}
270       else if (flags & S390_OPERAND_CR)
271 	{
272 	  info->fprintf_styled_func (info->stream, dis_style_text,
273 				     "%c", separator);
274 	  info->fprintf_styled_func (info->stream, dis_style_register,
275 				     "%%c%u", val.u);
276 	}
277       else if (flags & S390_OPERAND_PCREL)
278 	{
279 	  bfd_vma target = memaddr + val.i + val.i;
280 
281 	  /* Provide info for jump visualization.  May be evaluated by p_a_f().  */
282 	  info->target = target;
283 
284 	  info->fprintf_styled_func (info->stream, dis_style_text,
285 				     "%c", separator);
286 	  info->print_address_func (target, info);
287 	}
288       else if (flags & S390_OPERAND_SIGNED)
289 	{
290 	  enum disassembler_style style;
291 
292 	  info->fprintf_styled_func (info->stream, dis_style_text,
293 				     "%c", separator);
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);
297 	}
298       else
299 	{
300 	  enum disassembler_style style;
301 
302 	  if (flags & S390_OPERAND_OR1)
303 	    val.u &= ~1;
304 	  if (flags & S390_OPERAND_OR2)
305 	    val.u &= ~2;
306 	  if (flags & S390_OPERAND_OR8)
307 	    val.u &= ~8;
308 
309 	  if ((opcode->flags & S390_INSTR_FLAG_OPTPARM)
310 	      && val.u == 0
311 	      && opindex[1] == 0)
312 	    break;
313 	  info->fprintf_styled_func (info->stream, dis_style_text,
314 				     "%c", separator);
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);
318 	}
319 
320       if (flags & S390_OPERAND_DISP)
321 	separator = '(';
322       else if (flags & S390_OPERAND_BASE)
323 	{
324 	  info->fprintf_styled_func (info->stream, dis_style_text, ")");
325 	  separator = ',';
326 	}
327       else
328 	separator = ',';
329     }
330 
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);
336 }
337 
338 /* Check whether opcode A's mask is more specific than that of B.  */
339 
340 static int
341 opcode_mask_more_specific (const struct s390_opcode *a,
342 			   const struct s390_opcode *b)
343 {
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]));
348 }
349 
350 /* Print a S390 instruction.  */
351 
352 int
353 print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
354 {
355   bfd_byte buffer[6];
356   const struct s390_opcode *opcode = NULL;
357   unsigned int value;
358   int status, opsize, bufsize, bytes_to_dump, i;
359 
360   /* The output looks better if we put 6 bytes on a line.  */
361   info->bytes_per_line = 6;
362 
363   /* Set some defaults for the insn info.  */
364   info->insn_info_valid    = 0;
365   info->branch_delay_insns = 0;
366   info->data_size          = 0;
367   info->insn_type          = dis_nonbranch;
368   info->target             = 0;
369   info->target2            = 0;
370 
371   /* Every S390 instruction is max 6 bytes long.  */
372   memset (buffer, 0, 6);
373   status = info->read_memory_func (memaddr, buffer, 6, info);
374   if (status != 0)
375     {
376       for (bufsize = 0; bufsize < 6; bufsize++)
377 	if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0)
378 	  break;
379       if (bufsize <= 0)
380 	{
381 	  info->memory_error_func (status, memaddr, info);
382 	  return -1;
383 	}
384       opsize = s390_insn_length (buffer);
385       status = opsize > bufsize;
386     }
387   else
388     {
389       bufsize = 6;
390       opsize = s390_insn_length (buffer);
391     }
392 
393   if (status == 0)
394     {
395       const struct s390_opcode *op;
396 
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];
401 	   op++)
402 	{
403 	  if ((op->modes & current_arch_mask)
404 	      && s390_insn_matches_opcode (buffer, op)
405 	      && (opcode == NULL
406 		  || opcode_mask_more_specific (op, opcode)))
407 	    opcode = op;
408 	}
409 
410       if (opcode != NULL)
411 	{
412 	  /* Provide info for jump visualization.  Must be done before print.  */
413 	  switch (opcode->flags & S390_INSTR_FLAG_CLASS_MASK)
414 	    {
415 	    case S390_INSTR_FLAGS_CLASS_JUMP:
416 	      info->insn_type = dis_branch;
417 	      break;
418 	    case S390_INSTR_FLAGS_CLASS_CONDJUMP:
419 	      info->insn_type = dis_condbranch;
420 	      break;
421 	    case S390_INSTR_FLAGS_CLASS_JUMPSR:
422 	      info->insn_type = dis_jsr;
423 	      break;
424 	    default:
425 	      info->insn_type = dis_nonbranch;
426 	    }
427 	  info->insn_info_valid = 1;
428 
429 	  /* The instruction is valid.  Print it and return its size.  */
430 	  s390_print_insn_with_opcode (memaddr, info, buffer, opcode);
431 	  return opsize;
432 	}
433     }
434 
435   /* For code sections it makes sense to skip unknown instructions
436      according to their length bits.  */
437   if (status == 0
438       && option_use_insn_len_bits_p
439       && info->section != NULL
440       && (info->section->flags & SEC_CODE))
441     bytes_to_dump = opsize;
442   else
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;
446 
447   if (bytes_to_dump == 0)
448     return 0;
449 
450   info->insn_type = dis_noninsn;
451   info->insn_info_valid = 1;
452 
453   /* Fall back to hex print.  */
454   switch (bytes_to_dump)
455     {
456     case 4:
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,
462 				 ".long");
463       info->fprintf_styled_func (info->stream, dis_style_text,
464 				 "\t");
465       info->fprintf_styled_func (info->stream, dis_style_immediate,
466 				 "0x%08x", value);
467       return 4;
468     case 2:
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,
472 				 ".short");
473       info->fprintf_styled_func (info->stream, dis_style_text,
474 				 "\t");
475       info->fprintf_styled_func (info->stream, dis_style_immediate,
476 				 "0x%04x", value);
477       return 2;
478     default:
479       info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
480 				 ".byte");
481       info->fprintf_styled_func (info->stream, dis_style_text,
482 				 "\t");
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;
489     }
490   return 0;
491 }
492 
493 const disasm_options_and_args_t *
494 disassembler_options_s390 (void)
495 {
496   static disasm_options_and_args_t *opts_and_args;
497 
498   if (opts_and_args == NULL)
499     {
500       size_t i, num_options = ARRAY_SIZE (options);
501       disasm_options_t *opts;
502 
503       opts_and_args = XNEW (disasm_options_and_args_t);
504       opts_and_args->args = NULL;
505 
506       opts = &opts_and_args->options;
507       opts->name = XNEWVEC (const char *, num_options + 1);
508       opts->description = XNEWVEC (const char *, num_options + 1);
509       opts->arg = NULL;
510       for (i = 0; i < num_options; i++)
511 	{
512 	  opts->name[i] = options[i].name;
513 	  opts->description[i] = _(options[i].description);
514 	}
515       /* The array we return must be NULL terminated.  */
516       opts->name[i] = NULL;
517       opts->description[i] = NULL;
518     }
519 
520   return opts_and_args;
521 }
522 
523 void
524 print_s390_disassembler_options (FILE *stream)
525 {
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"));
530 
531   for (i = 0; i < ARRAY_SIZE (options); i++)
532     {
533       unsigned int len = strlen (options[i].name);
534       if (max_len < len)
535 	max_len = len;
536     }
537 
538   for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
539     fprintf (stream, "  %s%*c %s\n",
540 	     options[i].name,
541 	     (int)(max_len - strlen (options[i].name)), ' ',
542 	     _(options[i].description));
543 }
544