1 /* xtensa-dis.c.  Disassembly functions for Xtensa.
2    Copyright (C) 2003-2024 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 #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
xtensa_coalesce_insn_tables(struct dis_private * priv)58 xtensa_coalesce_insn_tables (struct dis_private *priv)
59 {
60   const int mask = ~(XTENSA_PROP_DATA | XTENSA_PROP_NO_TRANSFORM);
61   int count = priv->insn_table_entry_count;
62   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   for (i = j = 0; j < count; ++i)
68     {
69       property_table_entry *entry = priv->insn_table_entries + i;
70 
71       *entry = priv->insn_table_entries[j];
72 
73       for (++j; j < count; ++j)
74           {
75             property_table_entry *next = priv->insn_table_entries + j;
76             int fill = xtensa_compute_fill_extra_space (entry);
77             int size = entry->size + fill;
78 
79             if (entry->address + size == next->address)
80               {
81                 int entry_flags = entry->flags & mask;
82                 int next_flags = next->flags & mask;
83 
84                 if (next_flags == entry_flags)
85                     entry->size = next->address - entry->address + next->size;
86                 else
87                     break;
88               }
89             else
90               {
91                 break;
92               }
93           }
94     }
95   priv->insn_table_entry_count = i;
96 }
97 
98 static property_table_entry *
xtensa_find_table_entry(bfd_vma memaddr,struct disassemble_info * info)99 xtensa_find_table_entry (bfd_vma memaddr, struct disassemble_info *info)
100 {
101   struct dis_private *priv = (struct dis_private *) info->private_data;
102   int i;
103 
104   if (priv->insn_table_entries == NULL
105       || priv->insn_table_entry_count < 0)
106     return NULL;
107 
108   if (memaddr < priv->insn_table_cur_addr)
109     priv->insn_table_cur_idx = 0;
110 
111   for (i = priv->insn_table_cur_idx; i < priv->insn_table_entry_count; ++i)
112     {
113       property_table_entry *block = priv->insn_table_entries + i;
114 
115       if (block->size != 0)
116           {
117             if ((memaddr >= block->address
118                  && memaddr < block->address + block->size)
119                 || memaddr < block->address)
120               {
121                 priv->insn_table_cur_addr = memaddr;
122                 priv->insn_table_cur_idx = i;
123                 return block;
124               }
125           }
126     }
127   return NULL;
128 }
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
xtensa_instruction_fits(bfd_vma memaddr,int size,property_table_entry * insn_block)135 xtensa_instruction_fits (bfd_vma memaddr, int size,
136                                property_table_entry *insn_block)
137 {
138   unsigned max_size;
139 
140   /* If no property table info, assume it fits.  */
141   if (insn_block == NULL || size <= 0)
142     return 1;
143 
144   /* If too high, limit nextstop by the next insn address.  */
145   if (insn_block->address > memaddr)
146     {
147       /* memaddr is not in an instruction block, but is followed by one.  */
148       max_size = insn_block->address - memaddr;
149     }
150   else
151     {
152       /* memaddr is in an instruction block, go no further than the end.  */
153       max_size = insn_block->address + insn_block->size - memaddr;
154     }
155 
156   /* Crossing a boundary, doesn't "fit".  */
157   if ((unsigned)size > max_size)
158     return 0;
159   return 1;
160 }
161 
162 static int
fetch_data(struct disassemble_info * info,bfd_vma memaddr)163 fetch_data (struct disassemble_info *info, bfd_vma memaddr)
164 {
165   int length, status = 0;
166   struct dis_private *priv = (struct dis_private *) info->private_data;
167   int insn_size = xtensa_isa_maxlength (xtensa_default_isa);
168 
169   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   memset (priv->byte_buf, 0, insn_size);
176   for (length = insn_size; length > 0; length--)
177     {
178       status = (*info->read_memory_func) (memaddr, priv->byte_buf, length,
179                                                     info);
180       if (status == 0)
181           return length;
182     }
183   (*info->memory_error_func) (status, memaddr, info);
184   OPCODES_SIGLONGJMP (priv->bailout, 1);
185   /*NOTREACHED*/
186 }
187 
188 
189 static void
print_xtensa_operand(bfd_vma memaddr,struct disassemble_info * info,xtensa_opcode opc,int opnd,unsigned operand_val)190 print_xtensa_operand (bfd_vma memaddr,
191                           struct disassemble_info *info,
192                           xtensa_opcode opc,
193                           int opnd,
194                           unsigned operand_val)
195 {
196   xtensa_isa isa = xtensa_default_isa;
197   int signed_operand_val, status;
198   bfd_byte litbuf[4];
199 
200   if (show_raw_fields)
201     {
202       if (operand_val < 0xa)
203           (*info->fprintf_func) (info->stream, "%u", operand_val);
204       else
205           (*info->fprintf_func) (info->stream, "0x%x", operand_val);
206       return;
207     }
208 
209   (void) xtensa_operand_decode (isa, opc, opnd, &operand_val);
210   signed_operand_val = (int) operand_val;
211 
212   if (xtensa_operand_is_register (isa, opc, opnd) == 0)
213     {
214       if (xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
215           {
216             (void) xtensa_operand_undo_reloc (isa, opc, opnd,
217                                                       &operand_val, memaddr);
218             info->target = operand_val;
219             (*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             if ((info->flags & INSN_HAS_RELOC) == 0
223                 && !strcmp ("l32r", xtensa_opcode_name (isa, opc)))
224               status = (*info->read_memory_func) (operand_val, litbuf, 4, info);
225             else
226               status = -1;
227 
228             if (status == 0)
229               {
230                 unsigned literal = bfd_get_bits (litbuf, 32,
231                                                          info->endian == BFD_ENDIAN_BIG);
232 
233                 (*info->fprintf_func) (info->stream, " (");
234                 (*info->print_address_func) (literal, info);
235                 (*info->fprintf_func) (info->stream, ")");
236               }
237           }
238       else
239           {
240             if ((signed_operand_val > -256) && (signed_operand_val < 256))
241               (*info->fprintf_styled_func) (info->stream, dis_style_immediate,
242                                                     "%d", signed_operand_val);
243             else
244               (*info->fprintf_styled_func) (info->stream, dis_style_immediate,
245                                                     "0x%x", signed_operand_val);
246           }
247     }
248   else
249     {
250       int i = 1;
251       xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
252       (*info->fprintf_styled_func) (info->stream, dis_style_register,
253                                             "%s%u",
254                                             xtensa_regfile_shortname (isa, opnd_rf),
255                                             operand_val);
256       while (i < xtensa_operand_num_regs (isa, opc, opnd))
257           {
258             operand_val++;
259             (*info->fprintf_styled_func) (info->stream, dis_style_register,
260                                                   ":%s%u",
261                                                   xtensa_regfile_shortname (isa, opnd_rf),
262                                                   operand_val);
263             i++;
264           }
265     }
266 }
267 
268 
269 /* Print the Xtensa instruction at address MEMADDR on info->stream.
270    Returns length of the instruction in bytes.  */
271 
272 int
print_insn_xtensa(bfd_vma memaddr,struct disassemble_info * info)273 print_insn_xtensa (bfd_vma memaddr, struct disassemble_info *info)
274 {
275   unsigned operand_val;
276   int bytes_fetched, size, maxsize, i, n, noperands, nslots;
277   xtensa_isa isa;
278   xtensa_opcode opc;
279   xtensa_format fmt;
280   static struct dis_private priv;
281   static bfd_byte *byte_buf = NULL;
282   static xtensa_insnbuf insn_buffer = NULL;
283   static xtensa_insnbuf slot_buffer = NULL;
284   int first, first_slot, valid_insn;
285   property_table_entry *insn_block;
286   enum dis_insn_type insn_type;
287   bfd_vma target;
288 
289   if (!xtensa_default_isa)
290     xtensa_default_isa = xtensa_isa_init (0, 0);
291 
292   info->target = 0;
293   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   info->bytes_per_line = MAX (maxsize, 4);
306 
307   /* Allocate buffers the first time through.  */
308   if (!insn_buffer)
309     {
310       insn_buffer = xtensa_insnbuf_alloc (xtensa_default_isa);
311       slot_buffer = xtensa_insnbuf_alloc (xtensa_default_isa);
312       byte_buf = (bfd_byte *) xmalloc (MAX (maxsize, 4));
313     }
314 
315   priv.byte_buf = byte_buf;
316 
317   info->private_data = (void *) &priv;
318 
319   /* Prepare instruction tables.  */
320 
321   if (info->section != NULL)
322     {
323       asection *section = info->section;
324 
325       if (priv.last_section != section)
326           {
327             bfd *abfd = section->owner;
328 
329             if (priv.last_section != NULL)
330               {
331                 /* Reset insn_table_entries.  */
332                 priv.insn_table_entry_count = 0;
333                 free (priv.insn_table_entries);
334                 priv.insn_table_entries = NULL;
335               }
336             priv.last_section = section;
337 
338             /* Read insn_table_entries.  */
339             priv.insn_table_entry_count =
340               xtensa_read_table_entries (abfd, section,
341                                                &priv.insn_table_entries,
342                                                XTENSA_PROP_SEC_NAME, false);
343             if (priv.insn_table_entry_count == 0)
344               {
345                 free (priv.insn_table_entries);
346                 priv.insn_table_entries = NULL;
347                 /* Backwards compatibility support.  */
348                 priv.insn_table_entry_count =
349                     xtensa_read_table_entries (abfd, section,
350                                                      &priv.insn_table_entries,
351                                                      XTENSA_INSN_SEC_NAME, false);
352               }
353             priv.insn_table_cur_idx = 0;
354             xtensa_coalesce_insn_tables (&priv);
355           }
356       /* Else nothing to do, same section as last time.  */
357     }
358 
359   if (OPCODES_SIGSETJMP (priv.bailout) != 0)
360       /* Error return.  */
361       return -1;
362 
363   /* Fetch the maximum size instruction.  */
364   bytes_fetched = fetch_data (info, memaddr);
365 
366   insn_block = xtensa_find_table_entry (memaddr, info);
367 
368   /* Don't set "isa" before the setjmp to keep the compiler from griping.  */
369   isa = xtensa_default_isa;
370   size = 0;
371   nslots = 0;
372   valid_insn = 0;
373   fmt = 0;
374   if (!insn_block || (insn_block->flags & XTENSA_PROP_INSN))
375     {
376       /* Copy the bytes into the decode buffer.  */
377       memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) *
378                                      sizeof (xtensa_insnbuf_word)));
379       xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf,
380                                          bytes_fetched);
381 
382       fmt = xtensa_format_decode (isa, insn_buffer);
383       if (fmt != XTENSA_UNDEFINED
384             && ((size = xtensa_format_length (isa, fmt)) <= bytes_fetched)
385             && xtensa_instruction_fits (memaddr, size, insn_block))
386           {
387             /* Make sure all the opcodes are valid.  */
388             valid_insn = 1;
389             nslots = xtensa_format_num_slots (isa, fmt);
390             for (n = 0; n < nslots; n++)
391               {
392                 xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer);
393                 if (xtensa_opcode_decode (isa, fmt, n, slot_buffer)
394                       == XTENSA_UNDEFINED)
395                     {
396                       valid_insn = 0;
397                       break;
398                     }
399               }
400           }
401     }
402 
403   if (!valid_insn)
404     {
405       if (insn_block && (insn_block->flags & XTENSA_PROP_LITERAL)
406             && (memaddr & 3) == 0 && bytes_fetched >= 4)
407           {
408             info->bytes_per_chunk = 4;
409             return 4;
410           }
411       else
412           {
413             (*info->fprintf_styled_func) (info->stream,
414                                                   dis_style_assembler_directive,
415                                                   ".byte");
416             (*info->fprintf_func) (info->stream, "\t");
417             (*info->fprintf_styled_func) (info->stream,
418                                                   dis_style_immediate,
419                                                   "%#02x", priv.byte_buf[0]);
420             return 1;
421           }
422     }
423 
424   if (nslots > 1)
425     (*info->fprintf_func) (info->stream, "{ ");
426 
427   insn_type = dis_nonbranch;
428   target = 0;
429   first_slot = 1;
430   for (n = 0; n < nslots; n++)
431     {
432       int imm_pcrel = 0;
433 
434       if (first_slot)
435           first_slot = 0;
436       else
437           (*info->fprintf_func) (info->stream, "; ");
438 
439       xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer);
440       opc = xtensa_opcode_decode (isa, fmt, n, slot_buffer);
441       (*info->fprintf_styled_func) (info->stream,
442                                             dis_style_mnemonic, "%s",
443                                             xtensa_opcode_name (isa, opc));
444 
445       if (xtensa_opcode_is_branch (isa, opc))
446           info->insn_type = dis_condbranch;
447       else if (xtensa_opcode_is_jump (isa, opc))
448           info->insn_type = dis_branch;
449       else if (xtensa_opcode_is_call (isa, opc))
450           info->insn_type = dis_jsr;
451       else
452           info->insn_type = dis_nonbranch;
453 
454       /* Print the operands (if any).  */
455       noperands = xtensa_opcode_num_operands (isa, opc);
456       first = 1;
457       for (i = 0; i < noperands; i++)
458           {
459             if (xtensa_operand_is_visible (isa, opc, i) == 0)
460               continue;
461             if (first)
462               {
463                 (*info->fprintf_func) (info->stream, "\t");
464                 first = 0;
465               }
466             else
467               (*info->fprintf_func) (info->stream, ", ");
468             (void) xtensa_operand_get_field (isa, opc, i, fmt, n,
469                                                      slot_buffer, &operand_val);
470 
471             print_xtensa_operand (memaddr, info, opc, i, operand_val);
472             if (xtensa_operand_is_PCrelative (isa, opc, i))
473               ++imm_pcrel;
474           }
475       if (!imm_pcrel)
476           info->insn_type = dis_nonbranch;
477       if (info->insn_type != dis_nonbranch)
478           {
479             insn_type = info->insn_type;
480             target = info->target;
481           }
482     }
483   info->insn_type = insn_type;
484   info->target = target;
485   info->insn_info_valid = 1;
486 
487   if (nslots > 1)
488     (*info->fprintf_func) (info->stream, " }");
489 
490   info->bytes_per_chunk = size;
491   info->display_endian = info->endian;
492 
493   return size;
494 }
495 
496