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