Coverage Report

Created: 2024-05-21 06:29

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