Coverage Report

Created: 2026-07-25 10:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/opcodes/xtensa-dis.c
Line
Count
Source
1
/* xtensa-dis.c.  Disassembly functions for Xtensa.
2
   Copyright (C) 2003-2026 Free Software Foundation, Inc.
3
   Contributed by Bob Wilson at Tensilica, Inc. (bwilson@tensilica.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 <stdlib.h>
24
#include <stdio.h>
25
#include <sys/types.h>
26
#include <string.h>
27
#include "xtensa-isa.h"
28
#include "ansidecl.h"
29
#include "libiberty.h"
30
#include "bfd.h"
31
#include "elf/xtensa.h"
32
#include "disassemble.h"
33
34
#include <setjmp.h>
35
36
extern xtensa_isa xtensa_default_isa;
37
38
#ifndef MAX
39
13.8M
#define MAX(a,b) (a > b ? a : b)
40
#endif
41
42
int show_raw_fields;
43
44
struct dis_private
45
{
46
  bfd_byte *byte_buf;
47
  OPCODES_SIGJMP_BUF bailout;
48
  /* Persistent fields, valid for last_section only.  */
49
  asection *last_section;
50
  property_table_entry *insn_table_entries;
51
  int insn_table_entry_count;
52
  /* Cached property table search position.  */
53
  bfd_vma insn_table_cur_addr;
54
  int insn_table_cur_idx;
55
};
56
57
static void
58
xtensa_coalesce_insn_tables (struct dis_private *priv)
59
6.25k
{
60
6.25k
  const int mask = ~(XTENSA_PROP_DATA | XTENSA_PROP_NO_TRANSFORM);
61
6.25k
  int count = priv->insn_table_entry_count;
62
6.25k
  int i, j;
63
64
  /* Loop over all entries, combining adjacent ones that differ only in
65
     the flag bits XTENSA_PROP_DATA and XTENSA_PROP_NO_TRANSFORM.  */
66
67
6.25k
  for (i = j = 0; j < count; ++i)
68
0
    {
69
0
      property_table_entry *entry = priv->insn_table_entries + i;
70
71
0
      *entry = priv->insn_table_entries[j];
72
73
0
      for (++j; j < count; ++j)
74
0
  {
75
0
    property_table_entry *next = priv->insn_table_entries + j;
76
0
    int fill = xtensa_compute_fill_extra_space (entry);
77
0
    int size = entry->size + fill;
78
79
0
    if (entry->address + size == next->address)
80
0
      {
81
0
        int entry_flags = entry->flags & mask;
82
0
        int next_flags = next->flags & mask;
83
84
0
        if (next_flags == entry_flags)
85
0
    entry->size = next->address - entry->address + next->size;
86
0
        else
87
0
    break;
88
0
      }
89
0
    else
90
0
      {
91
0
        break;
92
0
      }
93
0
  }
94
0
    }
95
6.25k
  priv->insn_table_entry_count = i;
96
6.25k
}
97
98
static property_table_entry *
99
xtensa_find_table_entry (bfd_vma memaddr, struct disassemble_info *info)
100
6.94M
{
101
6.94M
  struct dis_private *priv = (struct dis_private *) info->private_data;
102
6.94M
  int i;
103
104
6.94M
  if (priv->insn_table_entries == NULL
105
0
      || priv->insn_table_entry_count < 0)
106
6.94M
    return NULL;
107
108
0
  if (memaddr < priv->insn_table_cur_addr)
109
0
    priv->insn_table_cur_idx = 0;
110
111
0
  for (i = priv->insn_table_cur_idx; i < priv->insn_table_entry_count; ++i)
112
0
    {
113
0
      property_table_entry *block = priv->insn_table_entries + i;
114
115
0
      if (block->size != 0)
116
0
  {
117
0
    if ((memaddr >= block->address
118
0
         && memaddr < block->address + block->size)
119
0
        || memaddr < block->address)
120
0
      {
121
0
        priv->insn_table_cur_addr = memaddr;
122
0
        priv->insn_table_cur_idx = i;
123
0
        return block;
124
0
      }
125
0
  }
126
0
    }
127
0
  return NULL;
128
0
}
129
130
/* Check whether an instruction crosses an instruction block boundary
131
   (according to property tables).
132
   If it does, return 0 (doesn't fit), else return 1.  */
133
134
static int
135
xtensa_instruction_fits (bfd_vma memaddr, int size,
136
       property_table_entry *insn_block)
137
6.20M
{
138
6.20M
  unsigned max_size;
139
140
  /* If no property table info, assume it fits.  */
141
6.20M
  if (insn_block == NULL || size <= 0)
142
6.20M
    return 1;
143
144
  /* If too high, limit nextstop by the next insn address.  */
145
0
  if (insn_block->address > memaddr)
146
0
    {
147
      /* memaddr is not in an instruction block, but is followed by one.  */
148
0
      max_size = insn_block->address - memaddr;
149
0
    }
150
0
  else
151
0
    {
152
      /* memaddr is in an instruction block, go no further than the end.  */
153
0
      max_size = insn_block->address + insn_block->size - memaddr;
154
0
    }
155
156
  /* Crossing a boundary, doesn't "fit".  */
157
0
  if ((unsigned)size > max_size)
158
0
    return 0;
159
0
  return 1;
160
0
}
161
162
static int
163
fetch_data (struct disassemble_info *info, bfd_vma memaddr)
164
6.94M
{
165
6.94M
  int length, status = 0;
166
6.94M
  struct dis_private *priv = (struct dis_private *) info->private_data;
167
6.94M
  int insn_size = xtensa_isa_maxlength (xtensa_default_isa);
168
169
6.94M
  insn_size = MAX (insn_size, 4);
170
171
  /* Read the maximum instruction size, padding with zeros if we go past
172
     the end of the text section.  This code will automatically adjust
173
     length when we hit the end of the buffer.  */
174
175
6.94M
  memset (priv->byte_buf, 0, insn_size);
176
7.04M
  for (length = insn_size; length > 0; length--)
177
7.04M
    {
178
7.04M
      status = (*info->read_memory_func) (memaddr, priv->byte_buf, length,
179
7.04M
            info);
180
7.04M
      if (status == 0)
181
6.94M
  return length;
182
7.04M
    }
183
1
  (*info->memory_error_func) (status, memaddr, info);
184
1
  OPCODES_SIGLONGJMP (priv->bailout, 1);
185
  /*NOTREACHED*/
186
6.94M
}
187
188
189
static void
190
print_xtensa_operand (bfd_vma memaddr,
191
          struct disassemble_info *info,
192
          xtensa_opcode opc,
193
          int opnd,
194
          unsigned operand_val)
195
9.98M
{
196
9.98M
  xtensa_isa isa = xtensa_default_isa;
197
9.98M
  int signed_operand_val, status;
198
9.98M
  bfd_byte litbuf[4];
199
200
9.98M
  if (show_raw_fields)
201
0
    {
202
0
      if (operand_val < 0xa)
203
0
  (*info->fprintf_func) (info->stream, "%u", operand_val);
204
0
      else
205
0
  (*info->fprintf_func) (info->stream, "0x%x", operand_val);
206
0
      return;
207
0
    }
208
209
9.98M
  (void) xtensa_operand_decode (isa, opc, opnd, &operand_val);
210
9.98M
  signed_operand_val = (int) operand_val;
211
212
9.98M
  if (xtensa_operand_is_register (isa, opc, opnd) == 0)
213
3.33M
    {
214
3.33M
      if (xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
215
1.23M
  {
216
1.23M
    (void) xtensa_operand_undo_reloc (isa, opc, opnd,
217
1.23M
              &operand_val, memaddr);
218
1.23M
    info->target = operand_val;
219
1.23M
    (*info->print_address_func) (info->target, info);
220
    /*  Also display value loaded by L32R (but not if reloc exists,
221
        those tend to be wrong):  */
222
1.23M
    if ((info->flags & INSN_HAS_RELOC) == 0
223
1.23M
        && !strcmp ("l32r", xtensa_opcode_name (isa, opc)))
224
400k
      status = (*info->read_memory_func) (operand_val, litbuf, 4, info);
225
836k
    else
226
836k
      status = -1;
227
228
1.23M
    if (status == 0)
229
5.93k
      {
230
5.93k
        unsigned literal = bfd_get_bits (litbuf, 32,
231
5.93k
                 info->endian == BFD_ENDIAN_BIG);
232
233
5.93k
        (*info->fprintf_func) (info->stream, " (");
234
5.93k
        (*info->print_address_func) (literal, info);
235
5.93k
        (*info->fprintf_func) (info->stream, ")");
236
5.93k
      }
237
1.23M
  }
238
2.09M
      else
239
2.09M
  {
240
2.09M
    if ((signed_operand_val > -256) && (signed_operand_val < 256))
241
1.88M
      (*info->fprintf_styled_func) (info->stream, dis_style_immediate,
242
1.88M
            "%d", signed_operand_val);
243
206k
    else
244
206k
      (*info->fprintf_styled_func) (info->stream, dis_style_immediate,
245
206k
            "0x%x", signed_operand_val);
246
2.09M
  }
247
3.33M
    }
248
6.65M
  else
249
6.65M
    {
250
6.65M
      int i = 1;
251
6.65M
      xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
252
6.65M
      (*info->fprintf_styled_func) (info->stream, dis_style_register,
253
6.65M
            "%s%u",
254
6.65M
            xtensa_regfile_shortname (isa, opnd_rf),
255
6.65M
            operand_val);
256
6.80M
      while (i < xtensa_operand_num_regs (isa, opc, opnd))
257
146k
  {
258
146k
    operand_val++;
259
146k
    (*info->fprintf_styled_func) (info->stream, dis_style_register,
260
146k
          ":%s%u",
261
146k
          xtensa_regfile_shortname (isa, opnd_rf),
262
146k
          operand_val);
263
146k
    i++;
264
146k
  }
265
6.65M
    }
266
9.98M
}
267
268
269
/* Print the Xtensa instruction at address MEMADDR on info->stream.
270
   Returns length of the instruction in bytes.  */
271
272
int
273
print_insn_xtensa (bfd_vma memaddr, struct disassemble_info *info)
274
6.94M
{
275
6.94M
  unsigned operand_val;
276
6.94M
  int bytes_fetched, size, maxsize, i, n, noperands, nslots;
277
6.94M
  xtensa_isa isa;
278
6.94M
  xtensa_opcode opc;
279
6.94M
  xtensa_format fmt;
280
6.94M
  static struct dis_private priv;
281
6.94M
  static bfd_byte *byte_buf = NULL;
282
6.94M
  static xtensa_insnbuf insn_buffer = NULL;
283
6.94M
  static xtensa_insnbuf slot_buffer = NULL;
284
6.94M
  int first, first_slot, valid_insn;
285
6.94M
  property_table_entry *insn_block;
286
6.94M
  enum dis_insn_type insn_type;
287
6.94M
  bfd_vma target;
288
289
6.94M
  if (!xtensa_default_isa)
290
2
    xtensa_default_isa = xtensa_isa_init (0, 0);
291
292
6.94M
  info->target = 0;
293
6.94M
  maxsize = xtensa_isa_maxlength (xtensa_default_isa);
294
295
  /* Set bytes_per_line to control the amount of whitespace between the hex
296
     values and the opcode.  For Xtensa, we always print one "chunk" and we
297
     vary bytes_per_chunk to determine how many bytes to print.  (objdump
298
     would apparently prefer that we set bytes_per_chunk to 1 and vary
299
     bytes_per_line but that makes it hard to fit 64-bit instructions on
300
     an 80-column screen.)  The value of bytes_per_line here is not exactly
301
     right, because objdump adds an extra space for each chunk so that the
302
     amount of whitespace depends on the chunk size.  Oh well, it's good
303
     enough....  Note that we set the minimum size to 4 to accomodate
304
     literal pools.  */
305
6.94M
  info->bytes_per_line = MAX (maxsize, 4);
306
307
  /* Allocate buffers the first time through.  */
308
6.94M
  if (!insn_buffer)
309
2
    {
310
2
      insn_buffer = xtensa_insnbuf_alloc (xtensa_default_isa);
311
2
      slot_buffer = xtensa_insnbuf_alloc (xtensa_default_isa);
312
2
      byte_buf = (bfd_byte *) xmalloc (MAX (maxsize, 4));
313
2
    }
314
315
6.94M
  priv.byte_buf = byte_buf;
316
317
6.94M
  info->private_data = (void *) &priv;
318
319
  /* Prepare instruction tables.  */
320
321
6.94M
  if (info->section != NULL)
322
4.74M
    {
323
4.74M
      asection *section = info->section;
324
325
4.74M
      if (priv.last_section != section)
326
6.25k
  {
327
6.25k
    bfd *abfd = section->owner;
328
329
6.25k
    if (priv.last_section != NULL)
330
6.25k
      {
331
        /* Reset insn_table_entries.  */
332
6.25k
        priv.insn_table_entry_count = 0;
333
6.25k
        free (priv.insn_table_entries);
334
6.25k
        priv.insn_table_entries = NULL;
335
6.25k
      }
336
6.25k
    priv.last_section = section;
337
338
    /* Read insn_table_entries.  */
339
6.25k
    priv.insn_table_entry_count =
340
6.25k
      xtensa_read_table_entries (abfd, section,
341
6.25k
               &priv.insn_table_entries,
342
6.25k
               XTENSA_PROP_SEC_NAME, false);
343
6.25k
    if (priv.insn_table_entry_count == 0)
344
6.25k
      {
345
6.25k
        free (priv.insn_table_entries);
346
6.25k
        priv.insn_table_entries = NULL;
347
        /* Backwards compatibility support.  */
348
6.25k
        priv.insn_table_entry_count =
349
6.25k
    xtensa_read_table_entries (abfd, section,
350
6.25k
             &priv.insn_table_entries,
351
6.25k
             XTENSA_INSN_SEC_NAME, false);
352
6.25k
      }
353
6.25k
    priv.insn_table_cur_idx = 0;
354
6.25k
    xtensa_coalesce_insn_tables (&priv);
355
6.25k
  }
356
      /* Else nothing to do, same section as last time.  */
357
4.74M
    }
358
359
6.94M
  if (OPCODES_SIGSETJMP (priv.bailout) != 0)
360
      /* Error return.  */
361
1
      return -1;
362
363
  /* Fetch the maximum size instruction.  */
364
6.94M
  bytes_fetched = fetch_data (info, memaddr);
365
366
6.94M
  insn_block = xtensa_find_table_entry (memaddr, info);
367
368
  /* Don't set "isa" before the setjmp to keep the compiler from griping.  */
369
6.94M
  isa = xtensa_default_isa;
370
6.94M
  size = 0;
371
6.94M
  nslots = 0;
372
6.94M
  valid_insn = 0;
373
6.94M
  fmt = 0;
374
6.94M
  if (!insn_block || (insn_block->flags & XTENSA_PROP_INSN))
375
6.94M
    {
376
      /* Copy the bytes into the decode buffer.  */
377
6.94M
      memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) *
378
6.94M
             sizeof (xtensa_insnbuf_word)));
379
6.94M
      xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf,
380
6.94M
         bytes_fetched);
381
382
6.94M
      fmt = xtensa_format_decode (isa, insn_buffer);
383
6.94M
      if (fmt != XTENSA_UNDEFINED
384
6.21M
    && ((size = xtensa_format_length (isa, fmt)) <= bytes_fetched)
385
6.20M
    && xtensa_instruction_fits (memaddr, size, insn_block))
386
6.20M
  {
387
    /* Make sure all the opcodes are valid.  */
388
6.20M
    valid_insn = 1;
389
6.20M
    nslots = xtensa_format_num_slots (isa, fmt);
390
13.2M
    for (n = 0; n < nslots; n++)
391
7.03M
      {
392
7.03M
        xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer);
393
7.03M
        if (xtensa_opcode_decode (isa, fmt, n, slot_buffer)
394
7.03M
      == XTENSA_UNDEFINED)
395
0
    {
396
0
      valid_insn = 0;
397
0
      break;
398
0
    }
399
7.03M
      }
400
6.20M
  }
401
6.94M
    }
402
403
6.94M
  if (!valid_insn)
404
732k
    {
405
732k
      if (insn_block && (insn_block->flags & XTENSA_PROP_LITERAL)
406
0
    && (memaddr & 3) == 0 && bytes_fetched >= 4)
407
0
  {
408
0
    info->bytes_per_chunk = 4;
409
0
    return 4;
410
0
  }
411
732k
      else
412
732k
  {
413
732k
    (*info->fprintf_styled_func) (info->stream,
414
732k
          dis_style_assembler_directive,
415
732k
          ".byte");
416
732k
    (*info->fprintf_func) (info->stream, "\t");
417
732k
    (*info->fprintf_styled_func) (info->stream,
418
732k
          dis_style_immediate,
419
732k
          "%#02x", priv.byte_buf[0]);
420
732k
    return 1;
421
732k
  }
422
732k
    }
423
424
6.20M
  if (nslots > 1)
425
563k
    (*info->fprintf_func) (info->stream, "{ ");
426
427
6.20M
  insn_type = dis_nonbranch;
428
6.20M
  target = 0;
429
6.20M
  first_slot = 1;
430
13.2M
  for (n = 0; n < nslots; n++)
431
7.03M
    {
432
7.03M
      int imm_pcrel = 0;
433
434
7.03M
      if (first_slot)
435
6.20M
  first_slot = 0;
436
823k
      else
437
823k
  (*info->fprintf_func) (info->stream, "; ");
438
439
7.03M
      xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer);
440
7.03M
      opc = xtensa_opcode_decode (isa, fmt, n, slot_buffer);
441
7.03M
      (*info->fprintf_styled_func) (info->stream,
442
7.03M
            dis_style_mnemonic, "%s",
443
7.03M
            xtensa_opcode_name (isa, opc));
444
445
7.03M
      if (xtensa_opcode_is_branch (isa, opc))
446
474k
  info->insn_type = dis_condbranch;
447
6.55M
      else if (xtensa_opcode_is_jump (isa, opc))
448
136k
  info->insn_type = dis_branch;
449
6.42M
      else if (xtensa_opcode_is_call (isa, opc))
450
255k
  info->insn_type = dis_jsr;
451
6.16M
      else
452
6.16M
  info->insn_type = dis_nonbranch;
453
454
      /* Print the operands (if any).  */
455
7.03M
      noperands = xtensa_opcode_num_operands (isa, opc);
456
7.03M
      first = 1;
457
17.3M
      for (i = 0; i < noperands; i++)
458
10.2M
  {
459
10.2M
    if (xtensa_operand_is_visible (isa, opc, i) == 0)
460
291k
      continue;
461
9.98M
    if (first)
462
3.88M
      {
463
3.88M
        (*info->fprintf_func) (info->stream, "\t");
464
3.88M
        first = 0;
465
3.88M
      }
466
6.10M
    else
467
6.10M
      (*info->fprintf_func) (info->stream, ", ");
468
9.98M
    (void) xtensa_operand_get_field (isa, opc, i, fmt, n,
469
9.98M
             slot_buffer, &operand_val);
470
471
9.98M
    print_xtensa_operand (memaddr, info, opc, i, operand_val);
472
9.98M
    if (xtensa_operand_is_PCrelative (isa, opc, i))
473
1.23M
      ++imm_pcrel;
474
9.98M
  }
475
7.03M
      if (!imm_pcrel)
476
5.79M
  info->insn_type = dis_nonbranch;
477
7.03M
      if (info->insn_type != dis_nonbranch)
478
832k
  {
479
832k
    insn_type = info->insn_type;
480
832k
    target = info->target;
481
832k
  }
482
7.03M
    }
483
6.20M
  info->insn_type = insn_type;
484
6.20M
  info->target = target;
485
6.20M
  info->insn_info_valid = 1;
486
487
6.20M
  if (nslots > 1)
488
563k
    (*info->fprintf_func) (info->stream, " }");
489
490
6.20M
  info->bytes_per_chunk = size;
491
6.20M
  info->display_endian = info->endian;
492
493
6.20M
  return size;
494
6.94M
}
495