]>
Commit | Line | Data |
---|---|---|
e0001a05 | 1 | /* xtensa-dis.c. Disassembly functions for Xtensa. |
fd67aa11 | 2 | Copyright (C) 2003-2024 Free Software Foundation, Inc. |
e0001a05 NC |
3 | Contributed by Bob Wilson at Tensilica, Inc. (bwilson@tensilica.com) |
4 | ||
9b201bb5 | 5 | This file is part of the GNU opcodes library. |
e0001a05 | 6 | |
9b201bb5 NC |
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. | |
e0001a05 | 11 | |
9b201bb5 NC |
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. | |
e0001a05 | 16 | |
9b201bb5 NC |
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. */ | |
e0001a05 | 21 | |
df7b86aa | 22 | #include "sysdep.h" |
e0001a05 NC |
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" | |
43cd72b9 | 29 | #include "libiberty.h" |
4b8e28c7 MF |
30 | #include "bfd.h" |
31 | #include "elf/xtensa.h" | |
88c1242d | 32 | #include "disassemble.h" |
e0001a05 NC |
33 | |
34 | #include <setjmp.h> | |
35 | ||
43cd72b9 BW |
36 | extern xtensa_isa xtensa_default_isa; |
37 | ||
e0001a05 NC |
38 | #ifndef MAX |
39 | #define MAX(a,b) (a > b ? a : b) | |
40 | #endif | |
41 | ||
e0001a05 NC |
42 | int show_raw_fields; |
43 | ||
43cd72b9 BW |
44 | struct dis_private |
45 | { | |
e0001a05 | 46 | bfd_byte *byte_buf; |
8df14d78 | 47 | OPCODES_SIGJMP_BUF bailout; |
4b8e28c7 MF |
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; | |
e0001a05 NC |
55 | }; |
56 | ||
4b8e28c7 MF |
57 | static void |
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 * | |
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 | |
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 | } | |
43cd72b9 | 161 | |
e0001a05 | 162 | static int |
7fa3d080 | 163 | fetch_data (struct disassemble_info *info, bfd_vma memaddr) |
e0001a05 NC |
164 | { |
165 | int length, status = 0; | |
166 | struct dis_private *priv = (struct dis_private *) info->private_data; | |
43cd72b9 | 167 | int insn_size = xtensa_isa_maxlength (xtensa_default_isa); |
e0001a05 | 168 | |
4b8e28c7 MF |
169 | insn_size = MAX (insn_size, 4); |
170 | ||
e0001a05 NC |
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); | |
8df14d78 | 184 | OPCODES_SIGLONGJMP (priv->bailout, 1); |
e0001a05 NC |
185 | /*NOTREACHED*/ |
186 | } | |
187 | ||
188 | ||
189 | static void | |
7fa3d080 BW |
190 | print_xtensa_operand (bfd_vma memaddr, |
191 | struct disassemble_info *info, | |
192 | xtensa_opcode opc, | |
193 | int opnd, | |
194 | unsigned operand_val) | |
e0001a05 | 195 | { |
43cd72b9 | 196 | xtensa_isa isa = xtensa_default_isa; |
b3ea7639 MF |
197 | int signed_operand_val, status; |
198 | bfd_byte litbuf[4]; | |
43e65147 | 199 | |
e0001a05 NC |
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 | ||
43cd72b9 | 209 | (void) xtensa_operand_decode (isa, opc, opnd, &operand_val); |
e0001a05 NC |
210 | signed_operand_val = (int) operand_val; |
211 | ||
43cd72b9 | 212 | if (xtensa_operand_is_register (isa, opc, opnd) == 0) |
e0001a05 | 213 | { |
43cd72b9 BW |
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); | |
b3ea7639 MF |
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 | } | |
43cd72b9 | 237 | } |
e0001a05 | 238 | else |
43cd72b9 BW |
239 | { |
240 | if ((signed_operand_val > -256) && (signed_operand_val < 256)) | |
39086586 MF |
241 | (*info->fprintf_styled_func) (info->stream, dis_style_immediate, |
242 | "%d", signed_operand_val); | |
43cd72b9 | 243 | else |
39086586 MF |
244 | (*info->fprintf_styled_func) (info->stream, dis_style_immediate, |
245 | "0x%x", signed_operand_val); | |
43cd72b9 | 246 | } |
e0001a05 NC |
247 | } |
248 | else | |
43cd72b9 BW |
249 | { |
250 | int i = 1; | |
251 | xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd); | |
39086586 MF |
252 | (*info->fprintf_styled_func) (info->stream, dis_style_register, |
253 | "%s%u", | |
254 | xtensa_regfile_shortname (isa, opnd_rf), | |
255 | operand_val); | |
43cd72b9 BW |
256 | while (i < xtensa_operand_num_regs (isa, opc, opnd)) |
257 | { | |
258 | operand_val++; | |
39086586 MF |
259 | (*info->fprintf_styled_func) (info->stream, dis_style_register, |
260 | ":%s%u", | |
261 | xtensa_regfile_shortname (isa, opnd_rf), | |
262 | operand_val); | |
43cd72b9 | 263 | i++; |
43e65147 | 264 | } |
43cd72b9 | 265 | } |
e0001a05 NC |
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 | |
7fa3d080 | 273 | print_insn_xtensa (bfd_vma memaddr, struct disassemble_info *info) |
e0001a05 NC |
274 | { |
275 | unsigned operand_val; | |
43cd72b9 | 276 | int bytes_fetched, size, maxsize, i, n, noperands, nslots; |
e0001a05 NC |
277 | xtensa_isa isa; |
278 | xtensa_opcode opc; | |
43cd72b9 | 279 | xtensa_format fmt; |
4b8e28c7 | 280 | static struct dis_private priv; |
e0001a05 NC |
281 | static bfd_byte *byte_buf = NULL; |
282 | static xtensa_insnbuf insn_buffer = NULL; | |
43cd72b9 | 283 | static xtensa_insnbuf slot_buffer = NULL; |
ce72cd46 | 284 | int first, first_slot, valid_insn; |
4b8e28c7 | 285 | property_table_entry *insn_block; |
e80512c8 MF |
286 | enum dis_insn_type insn_type; |
287 | bfd_vma target; | |
e0001a05 NC |
288 | |
289 | if (!xtensa_default_isa) | |
43cd72b9 | 290 | xtensa_default_isa = xtensa_isa_init (0, 0); |
e0001a05 NC |
291 | |
292 | info->target = 0; | |
43cd72b9 | 293 | maxsize = xtensa_isa_maxlength (xtensa_default_isa); |
e0001a05 NC |
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) | |
43cd72b9 BW |
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 | } | |
e0001a05 NC |
314 | |
315 | priv.byte_buf = byte_buf; | |
316 | ||
7fa3d080 | 317 | info->private_data = (void *) &priv; |
4b8e28c7 MF |
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; | |
d96bf37b | 333 | free (priv.insn_table_entries); |
4b8e28c7 MF |
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, | |
78933a4a | 342 | XTENSA_PROP_SEC_NAME, false); |
4b8e28c7 MF |
343 | if (priv.insn_table_entry_count == 0) |
344 | { | |
d96bf37b | 345 | free (priv.insn_table_entries); |
4b8e28c7 MF |
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, | |
78933a4a | 351 | XTENSA_INSN_SEC_NAME, false); |
4b8e28c7 MF |
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 | ||
8df14d78 | 359 | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
e0001a05 NC |
360 | /* Error return. */ |
361 | return -1; | |
362 | ||
e0001a05 | 363 | /* Fetch the maximum size instruction. */ |
118fecd3 | 364 | bytes_fetched = fetch_data (info, memaddr); |
e0001a05 | 365 | |
4b8e28c7 | 366 | insn_block = xtensa_find_table_entry (memaddr, info); |
43cd72b9 | 367 | |
ce72cd46 AM |
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; | |
4b8e28c7 | 374 | if (!insn_block || (insn_block->flags & XTENSA_PROP_INSN)) |
43cd72b9 | 375 | { |
4b8e28c7 MF |
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)) | |
43cd72b9 | 386 | { |
4b8e28c7 MF |
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++) | |
43cd72b9 | 391 | { |
4b8e28c7 MF |
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 | } | |
43cd72b9 BW |
399 | } |
400 | } | |
401 | } | |
e0001a05 | 402 | |
43cd72b9 | 403 | if (!valid_insn) |
e0001a05 | 404 | { |
4b8e28c7 MF |
405 | if (insn_block && (insn_block->flags & XTENSA_PROP_LITERAL) |
406 | && (memaddr & 3) == 0 && bytes_fetched >= 4) | |
407 | { | |
c1cbb7d8 | 408 | info->bytes_per_chunk = 4; |
4b8e28c7 MF |
409 | return 4; |
410 | } | |
411 | else | |
412 | { | |
39086586 MF |
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]); | |
4b8e28c7 MF |
420 | return 1; |
421 | } | |
e0001a05 NC |
422 | } |
423 | ||
43cd72b9 BW |
424 | if (nslots > 1) |
425 | (*info->fprintf_func) (info->stream, "{ "); | |
e0001a05 | 426 | |
e80512c8 MF |
427 | insn_type = dis_nonbranch; |
428 | target = 0; | |
43cd72b9 BW |
429 | first_slot = 1; |
430 | for (n = 0; n < nslots; n++) | |
e0001a05 | 431 | { |
e80512c8 MF |
432 | int imm_pcrel = 0; |
433 | ||
43cd72b9 BW |
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); | |
39086586 MF |
441 | (*info->fprintf_styled_func) (info->stream, |
442 | dis_style_mnemonic, "%s", | |
443 | xtensa_opcode_name (isa, opc)); | |
e0001a05 | 444 | |
6aee2cb2 MF |
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; | |
e80512c8 MF |
451 | else |
452 | info->insn_type = dis_nonbranch; | |
6aee2cb2 | 453 | |
43cd72b9 BW |
454 | /* Print the operands (if any). */ |
455 | noperands = xtensa_opcode_num_operands (isa, opc); | |
456 | first = 1; | |
e0001a05 NC |
457 | for (i = 0; i < noperands; i++) |
458 | { | |
43cd72b9 BW |
459 | if (xtensa_operand_is_visible (isa, opc, i) == 0) |
460 | continue; | |
e0001a05 | 461 | if (first) |
43cd72b9 BW |
462 | { |
463 | (*info->fprintf_func) (info->stream, "\t"); | |
464 | first = 0; | |
465 | } | |
e0001a05 NC |
466 | else |
467 | (*info->fprintf_func) (info->stream, ", "); | |
43cd72b9 BW |
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); | |
e80512c8 MF |
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; | |
43cd72b9 | 481 | } |
e0001a05 | 482 | } |
e80512c8 MF |
483 | info->insn_type = insn_type; |
484 | info->target = target; | |
485 | info->insn_info_valid = 1; | |
e0001a05 | 486 | |
43cd72b9 BW |
487 | if (nslots > 1) |
488 | (*info->fprintf_func) (info->stream, " }"); | |
489 | ||
e0001a05 NC |
490 | info->bytes_per_chunk = size; |
491 | info->display_endian = info->endian; | |
492 | ||
493 | return size; | |
494 | } | |
495 |