/src/binutils-gdb/opcodes/xtensa-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* xtensa-dis.c. Disassembly functions for Xtensa. |
2 | | Copyright (C) 2003-2025 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 | 12.6M | #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 | 2.88k | { |
60 | 2.88k | const int mask = ~(XTENSA_PROP_DATA | XTENSA_PROP_NO_TRANSFORM); |
61 | 2.88k | int count = priv->insn_table_entry_count; |
62 | 2.88k | 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 | 2.88k | 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 | 2.88k | priv->insn_table_entry_count = i; |
96 | 2.88k | } |
97 | | |
98 | | static property_table_entry * |
99 | | xtensa_find_table_entry (bfd_vma memaddr, struct disassemble_info *info) |
100 | 6.32M | { |
101 | 6.32M | struct dis_private *priv = (struct dis_private *) info->private_data; |
102 | 6.32M | int i; |
103 | | |
104 | 6.32M | if (priv->insn_table_entries == NULL |
105 | 6.32M | || priv->insn_table_entry_count < 0) |
106 | 6.32M | 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 | 5.76M | { |
138 | 5.76M | unsigned max_size; |
139 | | |
140 | | /* If no property table info, assume it fits. */ |
141 | 5.76M | if (insn_block == NULL || size <= 0) |
142 | 5.76M | 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.32M | { |
165 | 6.32M | int length, status = 0; |
166 | 6.32M | struct dis_private *priv = (struct dis_private *) info->private_data; |
167 | 6.32M | int insn_size = xtensa_isa_maxlength (xtensa_default_isa); |
168 | | |
169 | 6.32M | 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.32M | memset (priv->byte_buf, 0, insn_size); |
176 | 6.39M | for (length = insn_size; length > 0; length--) |
177 | 6.39M | { |
178 | 6.39M | status = (*info->read_memory_func) (memaddr, priv->byte_buf, length, |
179 | 6.39M | info); |
180 | 6.39M | if (status == 0) |
181 | 6.32M | return length; |
182 | 6.39M | } |
183 | 1 | (*info->memory_error_func) (status, memaddr, info); |
184 | 1 | OPCODES_SIGLONGJMP (priv->bailout, 1); |
185 | | /*NOTREACHED*/ |
186 | 6.32M | } |
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.63M | { |
196 | 9.63M | xtensa_isa isa = xtensa_default_isa; |
197 | 9.63M | int signed_operand_val, status; |
198 | 9.63M | bfd_byte litbuf[4]; |
199 | | |
200 | 9.63M | 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.63M | (void) xtensa_operand_decode (isa, opc, opnd, &operand_val); |
210 | 9.63M | signed_operand_val = (int) operand_val; |
211 | | |
212 | 9.63M | if (xtensa_operand_is_register (isa, opc, opnd) == 0) |
213 | 3.19M | { |
214 | 3.19M | if (xtensa_operand_is_PCrelative (isa, opc, opnd) == 1) |
215 | 1.15M | { |
216 | 1.15M | (void) xtensa_operand_undo_reloc (isa, opc, opnd, |
217 | 1.15M | &operand_val, memaddr); |
218 | 1.15M | info->target = operand_val; |
219 | 1.15M | (*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.15M | if ((info->flags & INSN_HAS_RELOC) == 0 |
223 | 1.15M | && !strcmp ("l32r", xtensa_opcode_name (isa, opc))) |
224 | 377k | status = (*info->read_memory_func) (operand_val, litbuf, 4, info); |
225 | 776k | else |
226 | 776k | status = -1; |
227 | | |
228 | 1.15M | if (status == 0) |
229 | 7.85k | { |
230 | 7.85k | unsigned literal = bfd_get_bits (litbuf, 32, |
231 | 7.85k | info->endian == BFD_ENDIAN_BIG); |
232 | | |
233 | 7.85k | (*info->fprintf_func) (info->stream, " ("); |
234 | 7.85k | (*info->print_address_func) (literal, info); |
235 | 7.85k | (*info->fprintf_func) (info->stream, ")"); |
236 | 7.85k | } |
237 | 1.15M | } |
238 | 2.04M | else |
239 | 2.04M | { |
240 | 2.04M | if ((signed_operand_val > -256) && (signed_operand_val < 256)) |
241 | 1.85M | (*info->fprintf_styled_func) (info->stream, dis_style_immediate, |
242 | 1.85M | "%d", signed_operand_val); |
243 | 189k | else |
244 | 189k | (*info->fprintf_styled_func) (info->stream, dis_style_immediate, |
245 | 189k | "0x%x", signed_operand_val); |
246 | 2.04M | } |
247 | 3.19M | } |
248 | 6.44M | else |
249 | 6.44M | { |
250 | 6.44M | int i = 1; |
251 | 6.44M | xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd); |
252 | 6.44M | (*info->fprintf_styled_func) (info->stream, dis_style_register, |
253 | 6.44M | "%s%u", |
254 | 6.44M | xtensa_regfile_shortname (isa, opnd_rf), |
255 | 6.44M | operand_val); |
256 | 6.55M | while (i < xtensa_operand_num_regs (isa, opc, opnd)) |
257 | 109k | { |
258 | 109k | operand_val++; |
259 | 109k | (*info->fprintf_styled_func) (info->stream, dis_style_register, |
260 | 109k | ":%s%u", |
261 | 109k | xtensa_regfile_shortname (isa, opnd_rf), |
262 | 109k | operand_val); |
263 | 109k | i++; |
264 | 109k | } |
265 | 6.44M | } |
266 | 9.63M | } |
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.32M | { |
275 | 6.32M | unsigned operand_val; |
276 | 6.32M | int bytes_fetched, size, maxsize, i, n, noperands, nslots; |
277 | 6.32M | xtensa_isa isa; |
278 | 6.32M | xtensa_opcode opc; |
279 | 6.32M | xtensa_format fmt; |
280 | 6.32M | static struct dis_private priv; |
281 | 6.32M | static bfd_byte *byte_buf = NULL; |
282 | 6.32M | static xtensa_insnbuf insn_buffer = NULL; |
283 | 6.32M | static xtensa_insnbuf slot_buffer = NULL; |
284 | 6.32M | int first, first_slot, valid_insn; |
285 | 6.32M | property_table_entry *insn_block; |
286 | 6.32M | enum dis_insn_type insn_type; |
287 | 6.32M | bfd_vma target; |
288 | | |
289 | 6.32M | if (!xtensa_default_isa) |
290 | 2 | xtensa_default_isa = xtensa_isa_init (0, 0); |
291 | | |
292 | 6.32M | info->target = 0; |
293 | 6.32M | 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.32M | info->bytes_per_line = MAX (maxsize, 4); |
306 | | |
307 | | /* Allocate buffers the first time through. */ |
308 | 6.32M | 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.32M | priv.byte_buf = byte_buf; |
316 | | |
317 | 6.32M | info->private_data = (void *) &priv; |
318 | | |
319 | | /* Prepare instruction tables. */ |
320 | | |
321 | 6.32M | if (info->section != NULL) |
322 | 4.53M | { |
323 | 4.53M | asection *section = info->section; |
324 | | |
325 | 4.53M | if (priv.last_section != section) |
326 | 2.88k | { |
327 | 2.88k | bfd *abfd = section->owner; |
328 | | |
329 | 2.88k | if (priv.last_section != NULL) |
330 | 2.88k | { |
331 | | /* Reset insn_table_entries. */ |
332 | 2.88k | priv.insn_table_entry_count = 0; |
333 | 2.88k | free (priv.insn_table_entries); |
334 | 2.88k | priv.insn_table_entries = NULL; |
335 | 2.88k | } |
336 | 2.88k | priv.last_section = section; |
337 | | |
338 | | /* Read insn_table_entries. */ |
339 | 2.88k | priv.insn_table_entry_count = |
340 | 2.88k | xtensa_read_table_entries (abfd, section, |
341 | 2.88k | &priv.insn_table_entries, |
342 | 2.88k | XTENSA_PROP_SEC_NAME, false); |
343 | 2.88k | if (priv.insn_table_entry_count == 0) |
344 | 2.88k | { |
345 | 2.88k | free (priv.insn_table_entries); |
346 | 2.88k | priv.insn_table_entries = NULL; |
347 | | /* Backwards compatibility support. */ |
348 | 2.88k | priv.insn_table_entry_count = |
349 | 2.88k | xtensa_read_table_entries (abfd, section, |
350 | 2.88k | &priv.insn_table_entries, |
351 | 2.88k | XTENSA_INSN_SEC_NAME, false); |
352 | 2.88k | } |
353 | 2.88k | priv.insn_table_cur_idx = 0; |
354 | 2.88k | xtensa_coalesce_insn_tables (&priv); |
355 | 2.88k | } |
356 | | /* Else nothing to do, same section as last time. */ |
357 | 4.53M | } |
358 | | |
359 | 6.32M | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
360 | | /* Error return. */ |
361 | 1 | return -1; |
362 | | |
363 | | /* Fetch the maximum size instruction. */ |
364 | 6.32M | bytes_fetched = fetch_data (info, memaddr); |
365 | | |
366 | 6.32M | 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.32M | isa = xtensa_default_isa; |
370 | 6.32M | size = 0; |
371 | 6.32M | nslots = 0; |
372 | 6.32M | valid_insn = 0; |
373 | 6.32M | fmt = 0; |
374 | 6.32M | if (!insn_block || (insn_block->flags & XTENSA_PROP_INSN)) |
375 | 6.32M | { |
376 | | /* Copy the bytes into the decode buffer. */ |
377 | 6.32M | memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) * |
378 | 6.32M | sizeof (xtensa_insnbuf_word))); |
379 | 6.32M | xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf, |
380 | 6.32M | bytes_fetched); |
381 | | |
382 | 6.32M | fmt = xtensa_format_decode (isa, insn_buffer); |
383 | 6.32M | if (fmt != XTENSA_UNDEFINED |
384 | 6.32M | && ((size = xtensa_format_length (isa, fmt)) <= bytes_fetched) |
385 | 6.32M | && xtensa_instruction_fits (memaddr, size, insn_block)) |
386 | 5.76M | { |
387 | | /* Make sure all the opcodes are valid. */ |
388 | 5.76M | valid_insn = 1; |
389 | 5.76M | nslots = xtensa_format_num_slots (isa, fmt); |
390 | 12.2M | for (n = 0; n < nslots; n++) |
391 | 6.46M | { |
392 | 6.46M | xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer); |
393 | 6.46M | if (xtensa_opcode_decode (isa, fmt, n, slot_buffer) |
394 | 6.46M | == XTENSA_UNDEFINED) |
395 | 0 | { |
396 | 0 | valid_insn = 0; |
397 | 0 | break; |
398 | 0 | } |
399 | 6.46M | } |
400 | 5.76M | } |
401 | 6.32M | } |
402 | | |
403 | 6.32M | if (!valid_insn) |
404 | 562k | { |
405 | 562k | if (insn_block && (insn_block->flags & XTENSA_PROP_LITERAL) |
406 | 562k | && (memaddr & 3) == 0 && bytes_fetched >= 4) |
407 | 0 | { |
408 | 0 | info->bytes_per_chunk = 4; |
409 | 0 | return 4; |
410 | 0 | } |
411 | 562k | else |
412 | 562k | { |
413 | 562k | (*info->fprintf_styled_func) (info->stream, |
414 | 562k | dis_style_assembler_directive, |
415 | 562k | ".byte"); |
416 | 562k | (*info->fprintf_func) (info->stream, "\t"); |
417 | 562k | (*info->fprintf_styled_func) (info->stream, |
418 | 562k | dis_style_immediate, |
419 | 562k | "%#02x", priv.byte_buf[0]); |
420 | 562k | return 1; |
421 | 562k | } |
422 | 562k | } |
423 | | |
424 | 5.76M | if (nslots > 1) |
425 | 479k | (*info->fprintf_func) (info->stream, "{ "); |
426 | | |
427 | 5.76M | insn_type = dis_nonbranch; |
428 | 5.76M | target = 0; |
429 | 5.76M | first_slot = 1; |
430 | 12.2M | for (n = 0; n < nslots; n++) |
431 | 6.46M | { |
432 | 6.46M | int imm_pcrel = 0; |
433 | | |
434 | 6.46M | if (first_slot) |
435 | 5.76M | first_slot = 0; |
436 | 707k | else |
437 | 707k | (*info->fprintf_func) (info->stream, "; "); |
438 | | |
439 | 6.46M | xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer); |
440 | 6.46M | opc = xtensa_opcode_decode (isa, fmt, n, slot_buffer); |
441 | 6.46M | (*info->fprintf_styled_func) (info->stream, |
442 | 6.46M | dis_style_mnemonic, "%s", |
443 | 6.46M | xtensa_opcode_name (isa, opc)); |
444 | | |
445 | 6.46M | if (xtensa_opcode_is_branch (isa, opc)) |
446 | 421k | info->insn_type = dis_condbranch; |
447 | 6.04M | else if (xtensa_opcode_is_jump (isa, opc)) |
448 | 132k | info->insn_type = dis_branch; |
449 | 5.91M | else if (xtensa_opcode_is_call (isa, opc)) |
450 | 249k | info->insn_type = dis_jsr; |
451 | 5.66M | else |
452 | 5.66M | info->insn_type = dis_nonbranch; |
453 | | |
454 | | /* Print the operands (if any). */ |
455 | 6.46M | noperands = xtensa_opcode_num_operands (isa, opc); |
456 | 6.46M | first = 1; |
457 | 16.3M | for (i = 0; i < noperands; i++) |
458 | 9.91M | { |
459 | 9.91M | if (xtensa_operand_is_visible (isa, opc, i) == 0) |
460 | 280k | continue; |
461 | 9.63M | if (first) |
462 | 3.73M | { |
463 | 3.73M | (*info->fprintf_func) (info->stream, "\t"); |
464 | 3.73M | first = 0; |
465 | 3.73M | } |
466 | 5.90M | else |
467 | 5.90M | (*info->fprintf_func) (info->stream, ", "); |
468 | 9.63M | (void) xtensa_operand_get_field (isa, opc, i, fmt, n, |
469 | 9.63M | slot_buffer, &operand_val); |
470 | | |
471 | 9.63M | print_xtensa_operand (memaddr, info, opc, i, operand_val); |
472 | 9.63M | if (xtensa_operand_is_PCrelative (isa, opc, i)) |
473 | 1.15M | ++imm_pcrel; |
474 | 9.63M | } |
475 | 6.46M | if (!imm_pcrel) |
476 | 5.31M | info->insn_type = dis_nonbranch; |
477 | 6.46M | if (info->insn_type != dis_nonbranch) |
478 | 772k | { |
479 | 772k | insn_type = info->insn_type; |
480 | 772k | target = info->target; |
481 | 772k | } |
482 | 6.46M | } |
483 | 5.76M | info->insn_type = insn_type; |
484 | 5.76M | info->target = target; |
485 | 5.76M | info->insn_info_valid = 1; |
486 | | |
487 | 5.76M | if (nslots > 1) |
488 | 479k | (*info->fprintf_func) (info->stream, " }"); |
489 | | |
490 | 5.76M | info->bytes_per_chunk = size; |
491 | 5.76M | info->display_endian = info->endian; |
492 | | |
493 | 5.76M | return size; |
494 | 6.32M | } |
495 | | |