Commit | Line | Data |
---|---|---|
e0001a05 | 1 | /* xtensa-dis.c. Disassembly functions for Xtensa. |
250d07de | 2 | Copyright (C) 2003-2021 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)) | |
241 | (*info->fprintf_func) (info->stream, "%d", signed_operand_val); | |
242 | else | |
243 | (*info->fprintf_func) (info->stream, "0x%x", signed_operand_val); | |
244 | } | |
e0001a05 NC |
245 | } |
246 | else | |
43cd72b9 BW |
247 | { |
248 | int i = 1; | |
249 | xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd); | |
250 | (*info->fprintf_func) (info->stream, "%s%u", | |
251 | xtensa_regfile_shortname (isa, opnd_rf), | |
252 | operand_val); | |
253 | while (i < xtensa_operand_num_regs (isa, opc, opnd)) | |
254 | { | |
255 | operand_val++; | |
256 | (*info->fprintf_func) (info->stream, ":%s%u", | |
257 | xtensa_regfile_shortname (isa, opnd_rf), | |
258 | operand_val); | |
259 | i++; | |
43e65147 | 260 | } |
43cd72b9 | 261 | } |
e0001a05 NC |
262 | } |
263 | ||
264 | ||
265 | /* Print the Xtensa instruction at address MEMADDR on info->stream. | |
266 | Returns length of the instruction in bytes. */ | |
267 | ||
268 | int | |
7fa3d080 | 269 | print_insn_xtensa (bfd_vma memaddr, struct disassemble_info *info) |
e0001a05 NC |
270 | { |
271 | unsigned operand_val; | |
43cd72b9 | 272 | int bytes_fetched, size, maxsize, i, n, noperands, nslots; |
e0001a05 NC |
273 | xtensa_isa isa; |
274 | xtensa_opcode opc; | |
43cd72b9 | 275 | xtensa_format fmt; |
4b8e28c7 | 276 | static struct dis_private priv; |
e0001a05 NC |
277 | static bfd_byte *byte_buf = NULL; |
278 | static xtensa_insnbuf insn_buffer = NULL; | |
43cd72b9 | 279 | static xtensa_insnbuf slot_buffer = NULL; |
ce72cd46 | 280 | int first, first_slot, valid_insn; |
4b8e28c7 | 281 | property_table_entry *insn_block; |
e0001a05 NC |
282 | |
283 | if (!xtensa_default_isa) | |
43cd72b9 | 284 | xtensa_default_isa = xtensa_isa_init (0, 0); |
e0001a05 NC |
285 | |
286 | info->target = 0; | |
43cd72b9 | 287 | maxsize = xtensa_isa_maxlength (xtensa_default_isa); |
e0001a05 NC |
288 | |
289 | /* Set bytes_per_line to control the amount of whitespace between the hex | |
290 | values and the opcode. For Xtensa, we always print one "chunk" and we | |
291 | vary bytes_per_chunk to determine how many bytes to print. (objdump | |
292 | would apparently prefer that we set bytes_per_chunk to 1 and vary | |
293 | bytes_per_line but that makes it hard to fit 64-bit instructions on | |
294 | an 80-column screen.) The value of bytes_per_line here is not exactly | |
295 | right, because objdump adds an extra space for each chunk so that the | |
296 | amount of whitespace depends on the chunk size. Oh well, it's good | |
297 | enough.... Note that we set the minimum size to 4 to accomodate | |
298 | literal pools. */ | |
299 | info->bytes_per_line = MAX (maxsize, 4); | |
300 | ||
301 | /* Allocate buffers the first time through. */ | |
302 | if (!insn_buffer) | |
43cd72b9 BW |
303 | { |
304 | insn_buffer = xtensa_insnbuf_alloc (xtensa_default_isa); | |
305 | slot_buffer = xtensa_insnbuf_alloc (xtensa_default_isa); | |
306 | byte_buf = (bfd_byte *) xmalloc (MAX (maxsize, 4)); | |
307 | } | |
e0001a05 NC |
308 | |
309 | priv.byte_buf = byte_buf; | |
310 | ||
7fa3d080 | 311 | info->private_data = (void *) &priv; |
4b8e28c7 MF |
312 | |
313 | /* Prepare instruction tables. */ | |
314 | ||
315 | if (info->section != NULL) | |
316 | { | |
317 | asection *section = info->section; | |
318 | ||
319 | if (priv.last_section != section) | |
320 | { | |
321 | bfd *abfd = section->owner; | |
322 | ||
323 | if (priv.last_section != NULL) | |
324 | { | |
325 | /* Reset insn_table_entries. */ | |
326 | priv.insn_table_entry_count = 0; | |
d96bf37b | 327 | free (priv.insn_table_entries); |
4b8e28c7 MF |
328 | priv.insn_table_entries = NULL; |
329 | } | |
330 | priv.last_section = section; | |
331 | ||
332 | /* Read insn_table_entries. */ | |
333 | priv.insn_table_entry_count = | |
334 | xtensa_read_table_entries (abfd, section, | |
335 | &priv.insn_table_entries, | |
78933a4a | 336 | XTENSA_PROP_SEC_NAME, false); |
4b8e28c7 MF |
337 | if (priv.insn_table_entry_count == 0) |
338 | { | |
d96bf37b | 339 | free (priv.insn_table_entries); |
4b8e28c7 MF |
340 | priv.insn_table_entries = NULL; |
341 | /* Backwards compatibility support. */ | |
342 | priv.insn_table_entry_count = | |
343 | xtensa_read_table_entries (abfd, section, | |
344 | &priv.insn_table_entries, | |
78933a4a | 345 | XTENSA_INSN_SEC_NAME, false); |
4b8e28c7 MF |
346 | } |
347 | priv.insn_table_cur_idx = 0; | |
348 | xtensa_coalesce_insn_tables (&priv); | |
349 | } | |
350 | /* Else nothing to do, same section as last time. */ | |
351 | } | |
352 | ||
8df14d78 | 353 | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
e0001a05 NC |
354 | /* Error return. */ |
355 | return -1; | |
356 | ||
e0001a05 | 357 | /* Fetch the maximum size instruction. */ |
118fecd3 | 358 | bytes_fetched = fetch_data (info, memaddr); |
e0001a05 | 359 | |
4b8e28c7 | 360 | insn_block = xtensa_find_table_entry (memaddr, info); |
43cd72b9 | 361 | |
ce72cd46 AM |
362 | /* Don't set "isa" before the setjmp to keep the compiler from griping. */ |
363 | isa = xtensa_default_isa; | |
364 | size = 0; | |
365 | nslots = 0; | |
366 | valid_insn = 0; | |
367 | fmt = 0; | |
4b8e28c7 | 368 | if (!insn_block || (insn_block->flags & XTENSA_PROP_INSN)) |
43cd72b9 | 369 | { |
4b8e28c7 MF |
370 | /* Copy the bytes into the decode buffer. */ |
371 | memset (insn_buffer, 0, (xtensa_insnbuf_size (isa) * | |
372 | sizeof (xtensa_insnbuf_word))); | |
373 | xtensa_insnbuf_from_chars (isa, insn_buffer, priv.byte_buf, | |
374 | bytes_fetched); | |
375 | ||
376 | fmt = xtensa_format_decode (isa, insn_buffer); | |
377 | if (fmt != XTENSA_UNDEFINED | |
378 | && ((size = xtensa_format_length (isa, fmt)) <= bytes_fetched) | |
379 | && xtensa_instruction_fits (memaddr, size, insn_block)) | |
43cd72b9 | 380 | { |
4b8e28c7 MF |
381 | /* Make sure all the opcodes are valid. */ |
382 | valid_insn = 1; | |
383 | nslots = xtensa_format_num_slots (isa, fmt); | |
384 | for (n = 0; n < nslots; n++) | |
43cd72b9 | 385 | { |
4b8e28c7 MF |
386 | xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer); |
387 | if (xtensa_opcode_decode (isa, fmt, n, slot_buffer) | |
388 | == XTENSA_UNDEFINED) | |
389 | { | |
390 | valid_insn = 0; | |
391 | break; | |
392 | } | |
43cd72b9 BW |
393 | } |
394 | } | |
395 | } | |
e0001a05 | 396 | |
43cd72b9 | 397 | if (!valid_insn) |
e0001a05 | 398 | { |
4b8e28c7 MF |
399 | if (insn_block && (insn_block->flags & XTENSA_PROP_LITERAL) |
400 | && (memaddr & 3) == 0 && bytes_fetched >= 4) | |
401 | { | |
c1cbb7d8 | 402 | info->bytes_per_chunk = 4; |
4b8e28c7 MF |
403 | return 4; |
404 | } | |
405 | else | |
406 | { | |
407 | (*info->fprintf_func) (info->stream, ".byte %#02x", priv.byte_buf[0]); | |
408 | return 1; | |
409 | } | |
e0001a05 NC |
410 | } |
411 | ||
43cd72b9 BW |
412 | if (nslots > 1) |
413 | (*info->fprintf_func) (info->stream, "{ "); | |
e0001a05 | 414 | |
6aee2cb2 MF |
415 | info->insn_type = dis_nonbranch; |
416 | info->insn_info_valid = 1; | |
417 | ||
43cd72b9 BW |
418 | first_slot = 1; |
419 | for (n = 0; n < nslots; n++) | |
e0001a05 | 420 | { |
43cd72b9 BW |
421 | if (first_slot) |
422 | first_slot = 0; | |
423 | else | |
424 | (*info->fprintf_func) (info->stream, "; "); | |
425 | ||
426 | xtensa_format_get_slot (isa, fmt, n, insn_buffer, slot_buffer); | |
427 | opc = xtensa_opcode_decode (isa, fmt, n, slot_buffer); | |
428 | (*info->fprintf_func) (info->stream, "%s", | |
429 | xtensa_opcode_name (isa, opc)); | |
e0001a05 | 430 | |
6aee2cb2 MF |
431 | if (xtensa_opcode_is_branch (isa, opc)) |
432 | info->insn_type = dis_condbranch; | |
433 | else if (xtensa_opcode_is_jump (isa, opc)) | |
434 | info->insn_type = dis_branch; | |
435 | else if (xtensa_opcode_is_call (isa, opc)) | |
436 | info->insn_type = dis_jsr; | |
437 | ||
43cd72b9 BW |
438 | /* Print the operands (if any). */ |
439 | noperands = xtensa_opcode_num_operands (isa, opc); | |
440 | first = 1; | |
e0001a05 NC |
441 | for (i = 0; i < noperands; i++) |
442 | { | |
43cd72b9 BW |
443 | if (xtensa_operand_is_visible (isa, opc, i) == 0) |
444 | continue; | |
e0001a05 | 445 | if (first) |
43cd72b9 BW |
446 | { |
447 | (*info->fprintf_func) (info->stream, "\t"); | |
448 | first = 0; | |
449 | } | |
e0001a05 NC |
450 | else |
451 | (*info->fprintf_func) (info->stream, ", "); | |
43cd72b9 BW |
452 | (void) xtensa_operand_get_field (isa, opc, i, fmt, n, |
453 | slot_buffer, &operand_val); | |
454 | ||
455 | print_xtensa_operand (memaddr, info, opc, i, operand_val); | |
456 | } | |
e0001a05 NC |
457 | } |
458 | ||
43cd72b9 BW |
459 | if (nslots > 1) |
460 | (*info->fprintf_func) (info->stream, " }"); | |
461 | ||
e0001a05 NC |
462 | info->bytes_per_chunk = size; |
463 | info->display_endian = info->endian; | |
464 | ||
465 | return size; | |
466 | } | |
467 |