xref: /netbsd-src/external/gpl3/binutils/dist/opcodes/s390-dis.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
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   { "zarch",      N_("Disassemble in z/Architecture mode") },
46   { "insnlength", N_("Print unknown instructions according to "
47 		     "length from first two bits") },
48   { "insndesc",   N_("Print instruction description as comment") },
49 };
50 
51 /* Set up index table for first opcode byte.  */
52 
53 void
disassemble_init_s390(struct disassemble_info * info)54 disassemble_init_s390 (struct disassemble_info *info)
55 {
56   int i;
57   const char *p;
58 
59   memset (opc_index, 0, sizeof (opc_index));
60 
61   /* Reverse order, such that each opc_index ends up pointing to the
62      first matching entry instead of the last.  */
63   for (i = s390_num_opcodes; i--; )
64     opc_index[s390_opcodes[i].opcode[0]] = i;
65 
66   current_arch_mask = 1 << S390_OPCODE_ZARCH;
67   option_use_insn_len_bits_p = 0;
68   option_print_insn_desc = 0;
69 
70   for (p = info->disassembler_options; p != NULL; )
71     {
72       if (startswith (p, "esa"))
73 	current_arch_mask = 1 << S390_OPCODE_ESA;
74       else if (startswith (p, "zarch"))
75 	current_arch_mask = 1 << S390_OPCODE_ZARCH;
76       else if (startswith (p, "insnlength"))
77 	option_use_insn_len_bits_p = 1;
78       else if (startswith (p, "insndesc"))
79 	option_print_insn_desc = 1;
80       else
81 	/* xgettext:c-format */
82 	opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p);
83 
84       p = strchr (p, ',');
85       if (p != NULL)
86 	p++;
87     }
88 }
89 
90 /* Derive the length of an instruction from its first byte.  */
91 
92 static inline int
s390_insn_length(const bfd_byte * buffer)93 s390_insn_length (const bfd_byte *buffer)
94 {
95   /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6.  */
96   return ((buffer[0] >> 6) + 3) & ~1U;
97 }
98 
99 /* Match the instruction in BUFFER against the given OPCODE, excluding
100    the first byte.  */
101 
102 static inline int
s390_insn_matches_opcode(const bfd_byte * buffer,const struct s390_opcode * opcode)103 s390_insn_matches_opcode (const bfd_byte *buffer,
104 			  const struct s390_opcode *opcode)
105 {
106   return (buffer[1] & opcode->mask[1]) == opcode->opcode[1]
107     && (buffer[2] & opcode->mask[2]) == opcode->opcode[2]
108     && (buffer[3] & opcode->mask[3]) == opcode->opcode[3]
109     && (buffer[4] & opcode->mask[4]) == opcode->opcode[4]
110     && (buffer[5] & opcode->mask[5]) == opcode->opcode[5];
111 }
112 
113 union operand_value
114 {
115   int i;
116   unsigned int u;
117 };
118 
119 /* Extracts an operand value from an instruction.  */
120 /* We do not perform the shift operation for larl-type address
121    operands here since that would lead to an overflow of the 32 bit
122    integer value.  Instead the shift operation is done when printing
123    the operand.  */
124 
125 static inline union operand_value
s390_extract_operand(const bfd_byte * insn,const struct s390_operand * operand)126 s390_extract_operand (const bfd_byte *insn,
127 		      const struct s390_operand *operand)
128 {
129   union operand_value ret;
130   unsigned int val;
131   int bits;
132   const bfd_byte *orig_insn = insn;
133 
134   /* Extract fragments of the operand byte for byte.  */
135   insn += operand->shift / 8;
136   bits = (operand->shift & 7) + operand->bits;
137   val = 0;
138   do
139     {
140       val <<= 8;
141       val |= (unsigned int) *insn++;
142       bits -= 8;
143     }
144   while (bits > 0);
145   val >>= -bits;
146   val &= ((1U << (operand->bits - 1)) << 1) - 1;
147 
148   /* Check for special long displacement case.  */
149   if (operand->bits == 20 && operand->shift == 20)
150     val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
151 
152   /* Sign extend value if the operand is signed or pc relative.  Avoid
153      integer overflows.  */
154   if (operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL))
155     {
156       unsigned int m = 1U << (operand->bits - 1);
157 
158       if (val >= m)
159 	ret.i = (int) (val - m) - 1 - (int) (m - 1U);
160       else
161 	ret.i = (int) val;
162     }
163   else if (operand->flags & S390_OPERAND_LENGTH)
164     /* Length x in an instruction has real length x + 1.  */
165     ret.u = val + 1;
166 
167   else if (operand->flags & S390_OPERAND_VR)
168     {
169       /* Extract the extra bits for a vector register operand stored
170 	 in the RXB field.  */
171       unsigned vr = operand->shift == 32 ? 3
172 	: (unsigned) operand->shift / 4 - 2;
173 
174       ret.u = val | ((orig_insn[4] & (1 << (3 - vr))) << (vr + 1));
175     }
176   else
177     ret.u = val;
178 
179   return ret;
180 }
181 
182 /* Print the S390 instruction in BUFFER, assuming that it matches the
183    given OPCODE.  */
184 
185 static void
s390_print_insn_with_opcode(bfd_vma memaddr,struct disassemble_info * info,const bfd_byte * buffer,const struct s390_opcode * opcode)186 s390_print_insn_with_opcode (bfd_vma memaddr,
187 			     struct disassemble_info *info,
188 			     const bfd_byte *buffer,
189 			     const struct s390_opcode *opcode)
190 {
191   const unsigned char *opindex;
192   char separator;
193 
194   /* Mnemonic.  */
195   info->fprintf_styled_func (info->stream, dis_style_mnemonic,
196 			     "%s", opcode->name);
197 
198   /* Operands.  */
199   separator = '\t';
200   for (opindex = opcode->operands; *opindex != 0; opindex++)
201     {
202       const struct s390_operand *operand = s390_operands + *opindex;
203       union operand_value val = s390_extract_operand (buffer, operand);
204       unsigned long flags = operand->flags;
205 
206       if ((flags & S390_OPERAND_INDEX) && val.u == 0)
207 	continue;
208       if ((flags & S390_OPERAND_BASE) &&
209 	  val.u == 0 && separator == '(')
210 	{
211 	  separator = ',';
212 	  continue;
213 	}
214 
215       /* For instructions with a last optional operand don't print it
216 	 if zero.  */
217       if ((opcode->flags & (S390_INSTR_FLAG_OPTPARM | S390_INSTR_FLAG_OPTPARM2))
218 	  && val.u == 0
219 	  && opindex[1] == 0)
220 	break;
221 
222       if ((opcode->flags & S390_INSTR_FLAG_OPTPARM2)
223 	  && val.u == 0 && opindex[1] != 0 && opindex[2] == 0)
224 	{
225 	  union operand_value next_op_val =
226 	    s390_extract_operand (buffer, s390_operands + opindex[1]);
227 	  if (next_op_val.u == 0)
228 	    break;
229 	}
230 
231       if (flags & S390_OPERAND_GPR)
232 	{
233 	  info->fprintf_styled_func (info->stream, dis_style_text,
234 				     "%c", separator);
235 	  info->fprintf_styled_func (info->stream, dis_style_register,
236 				     "%%r%u", val.u);
237 	}
238       else if (flags & S390_OPERAND_FPR)
239 	{
240 	  info->fprintf_styled_func (info->stream, dis_style_text,
241 				     "%c", separator);
242 	  info->fprintf_styled_func (info->stream, dis_style_register,
243 				     "%%f%u", val.u);
244 	}
245       else if (flags & S390_OPERAND_VR)
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 				     "%%v%i", val.u);
251 	}
252       else if (flags & S390_OPERAND_AR)
253 	{
254 	  info->fprintf_styled_func (info->stream, dis_style_text,
255 				     "%c", separator);
256 	  info->fprintf_styled_func (info->stream, dis_style_register,
257 				     "%%a%u", val.u);
258 	}
259       else if (flags & S390_OPERAND_CR)
260 	{
261 	  info->fprintf_styled_func (info->stream, dis_style_text,
262 				     "%c", separator);
263 	  info->fprintf_styled_func (info->stream, dis_style_register,
264 				     "%%c%u", val.u);
265 	}
266       else if (flags & S390_OPERAND_PCREL)
267 	{
268 	  bfd_vma target = memaddr + val.i + val.i;
269 
270 	  /* Provide info for jump visualization.  May be evaluated by p_a_f().  */
271 	  info->target = target;
272 
273 	  info->fprintf_styled_func (info->stream, dis_style_text,
274 				     "%c", separator);
275 	  info->print_address_func (target, info);
276 	}
277       else if (flags & S390_OPERAND_SIGNED)
278 	{
279 	  enum disassembler_style style;
280 
281 	  info->fprintf_styled_func (info->stream, dis_style_text,
282 				     "%c", separator);
283 	  style = ((flags & S390_OPERAND_DISP)
284 		   ? dis_style_address_offset : dis_style_immediate);
285 	  info->fprintf_styled_func (info->stream, style, "%i", val.i);
286 	}
287       else
288 	{
289 	  enum disassembler_style style;
290 
291 	  if (flags & S390_OPERAND_OR1)
292 	    val.u &= ~1;
293 	  if (flags & S390_OPERAND_OR2)
294 	    val.u &= ~2;
295 	  if (flags & S390_OPERAND_OR8)
296 	    val.u &= ~8;
297 
298 	  if ((opcode->flags & S390_INSTR_FLAG_OPTPARM)
299 	      && val.u == 0
300 	      && opindex[1] == 0)
301 	    break;
302 	  info->fprintf_styled_func (info->stream, dis_style_text,
303 				     "%c", separator);
304 	  style = ((flags & S390_OPERAND_DISP)
305 		   ? dis_style_address_offset : dis_style_immediate);
306 	  info->fprintf_styled_func (info->stream, style, "%u", val.u);
307 	}
308 
309       if (flags & S390_OPERAND_DISP)
310 	separator = '(';
311       else if (flags & S390_OPERAND_BASE)
312 	{
313 	  info->fprintf_styled_func (info->stream, dis_style_text, ")");
314 	  separator = ',';
315 	}
316       else
317 	separator = ',';
318     }
319 
320   /* Optional: instruction name.  */
321   if (option_print_insn_desc && opcode->description
322       && opcode->description[0] != '\0')
323     info->fprintf_styled_func (info->stream, dis_style_comment_start, "\t# %s",
324 			       opcode->description);
325 }
326 
327 /* Check whether opcode A's mask is more specific than that of B.  */
328 
329 static int
opcode_mask_more_specific(const struct s390_opcode * a,const struct s390_opcode * b)330 opcode_mask_more_specific (const struct s390_opcode *a,
331 			   const struct s390_opcode *b)
332 {
333   return (((int) a->mask[0] + a->mask[1] + a->mask[2]
334 	   + a->mask[3] + a->mask[4] + a->mask[5])
335 	  > ((int) b->mask[0] + b->mask[1] + b->mask[2]
336 	     + b->mask[3] + b->mask[4] + b->mask[5]));
337 }
338 
339 /* Print a S390 instruction.  */
340 
341 int
print_insn_s390(bfd_vma memaddr,struct disassemble_info * info)342 print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
343 {
344   bfd_byte buffer[6];
345   const struct s390_opcode *opcode = NULL;
346   unsigned int value;
347   int status, opsize, bufsize, bytes_to_dump, i;
348 
349   /* The output looks better if we put 6 bytes on a line.  */
350   info->bytes_per_line = 6;
351 
352   /* Set some defaults for the insn info.  */
353   info->insn_info_valid    = 0;
354   info->branch_delay_insns = 0;
355   info->data_size          = 0;
356   info->insn_type          = dis_nonbranch;
357   info->target             = 0;
358   info->target2            = 0;
359 
360   /* Every S390 instruction is max 6 bytes long.  */
361   memset (buffer, 0, 6);
362   status = info->read_memory_func (memaddr, buffer, 6, info);
363   if (status != 0)
364     {
365       for (bufsize = 0; bufsize < 6; bufsize++)
366 	if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0)
367 	  break;
368       if (bufsize <= 0)
369 	{
370 	  info->memory_error_func (status, memaddr, info);
371 	  return -1;
372 	}
373       opsize = s390_insn_length (buffer);
374       status = opsize > bufsize;
375     }
376   else
377     {
378       bufsize = 6;
379       opsize = s390_insn_length (buffer);
380     }
381 
382   if (status == 0)
383     {
384       const struct s390_opcode *op;
385 
386       /* Find the "best match" in the opcode table.  */
387       for (op = s390_opcodes + opc_index[buffer[0]];
388 	   op != s390_opcodes + s390_num_opcodes
389 	     && op->opcode[0] == buffer[0];
390 	   op++)
391 	{
392 	  if ((op->modes & current_arch_mask)
393 	      && s390_insn_matches_opcode (buffer, op)
394 	      && (opcode == NULL
395 		  || opcode_mask_more_specific (op, opcode)))
396 	    opcode = op;
397 	}
398 
399       if (opcode != NULL)
400 	{
401 	  /* Provide info for jump visualization.  Must be done before print.  */
402 	  switch (opcode->flags & S390_INSTR_FLAG_CLASS_MASK)
403 	    {
404 	    case S390_INSTR_FLAGS_CLASS_JUMP:
405 	      info->insn_type = dis_branch;
406 	      break;
407 	    case S390_INSTR_FLAGS_CLASS_CONDJUMP:
408 	      info->insn_type = dis_condbranch;
409 	      break;
410 	    case S390_INSTR_FLAGS_CLASS_JUMPSR:
411 	      info->insn_type = dis_jsr;
412 	      break;
413 	    default:
414 	      info->insn_type = dis_nonbranch;
415 	    }
416 	  info->insn_info_valid = 1;
417 
418 	  /* The instruction is valid.  Print it and return its size.  */
419 	  s390_print_insn_with_opcode (memaddr, info, buffer, opcode);
420 	  return opsize;
421 	}
422     }
423 
424   /* For code sections it makes sense to skip unknown instructions
425      according to their length bits.  */
426   if (status == 0
427       && option_use_insn_len_bits_p
428       && info->section != NULL
429       && (info->section->flags & SEC_CODE))
430     bytes_to_dump = opsize;
431   else
432     /* By default unknown instructions are printed as .long's/.short'
433        depending on how many bytes are available.  */
434     bytes_to_dump = bufsize >= 4 ? 4 : bufsize;
435 
436   if (bytes_to_dump == 0)
437     return 0;
438 
439   info->insn_type = dis_noninsn;
440   info->insn_info_valid = 1;
441 
442   /* Fall back to hex print.  */
443   switch (bytes_to_dump)
444     {
445     case 4:
446       value = (unsigned int) buffer[0];
447       value = (value << 8) + (unsigned int) buffer[1];
448       value = (value << 8) + (unsigned int) buffer[2];
449       value = (value << 8) + (unsigned int) buffer[3];
450       info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
451 				 ".long");
452       info->fprintf_styled_func (info->stream, dis_style_text,
453 				 "\t");
454       info->fprintf_styled_func (info->stream, dis_style_immediate,
455 				 "0x%08x", value);
456       return 4;
457     case 2:
458       value = (unsigned int) buffer[0];
459       value = (value << 8) + (unsigned int) buffer[1];
460       info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
461 				 ".short");
462       info->fprintf_styled_func (info->stream, dis_style_text,
463 				 "\t");
464       info->fprintf_styled_func (info->stream, dis_style_immediate,
465 				 "0x%04x", value);
466       return 2;
467     default:
468       info->fprintf_styled_func (info->stream, dis_style_assembler_directive,
469 				 ".byte");
470       info->fprintf_styled_func (info->stream, dis_style_text,
471 				 "\t");
472       info->fprintf_styled_func (info->stream, dis_style_immediate,
473 				 "0x%02x", (unsigned int) buffer[0]);
474       for (i = 1; i < bytes_to_dump; i++)
475 	info->fprintf_styled_func (info->stream, dis_style_immediate,
476 				   "0x%02x", (unsigned int) buffer[i]);
477       return bytes_to_dump;
478     }
479   return 0;
480 }
481 
482 const disasm_options_and_args_t *
disassembler_options_s390(void)483 disassembler_options_s390 (void)
484 {
485   static disasm_options_and_args_t *opts_and_args;
486 
487   if (opts_and_args == NULL)
488     {
489       size_t i, num_options = ARRAY_SIZE (options);
490       disasm_options_t *opts;
491 
492       opts_and_args = XNEW (disasm_options_and_args_t);
493       opts_and_args->args = NULL;
494 
495       opts = &opts_and_args->options;
496       opts->name = XNEWVEC (const char *, num_options + 1);
497       opts->description = XNEWVEC (const char *, num_options + 1);
498       opts->arg = NULL;
499       for (i = 0; i < num_options; i++)
500 	{
501 	  opts->name[i] = options[i].name;
502 	  opts->description[i] = _(options[i].description);
503 	}
504       /* The array we return must be NULL terminated.  */
505       opts->name[i] = NULL;
506       opts->description[i] = NULL;
507     }
508 
509   return opts_and_args;
510 }
511 
512 void
print_s390_disassembler_options(FILE * stream)513 print_s390_disassembler_options (FILE *stream)
514 {
515   unsigned int i, max_len = 0;
516   fprintf (stream, _("\n\
517 The following S/390 specific disassembler options are supported for use\n\
518 with the -M switch (multiple options should be separated by commas):\n"));
519 
520   for (i = 0; i < ARRAY_SIZE (options); i++)
521     {
522       unsigned int len = strlen (options[i].name);
523       if (max_len < len)
524 	max_len = len;
525     }
526 
527   for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
528     fprintf (stream, "  %s%*c %s\n",
529 	     options[i].name,
530 	     (int)(max_len - strlen (options[i].name)), ' ',
531 	     _(options[i].description));
532 }
533