/src/binutils-gdb/opcodes/vax-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Print VAX instructions. |
2 | | Copyright (C) 1995-2025 Free Software Foundation, Inc. |
3 | | Contributed by Pauline Middelink <middelin@polyware.iaf.nl> |
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 program; if not, write to the Free Software |
19 | | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
20 | | MA 02110-1301, USA. */ |
21 | | |
22 | | #include "sysdep.h" |
23 | | #include <setjmp.h> |
24 | | #include <string.h> |
25 | | #include "opcode/vax.h" |
26 | | #include "disassemble.h" |
27 | | |
28 | | static char *reg_names[] = |
29 | | { |
30 | | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
31 | | "r8", "r9", "r10", "r11", "ap", "fp", "sp", "pc" |
32 | | }; |
33 | | |
34 | | /* Definitions for the function entry mask bits. */ |
35 | | static char *entry_mask_bit[] = |
36 | | { |
37 | | /* Registers 0 and 1 shall not be saved, since they're used to pass back |
38 | | a function's result to its caller... */ |
39 | | "~r0~", "~r1~", |
40 | | /* Registers 2 .. 11 are normal registers. */ |
41 | | "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", |
42 | | /* Registers 12 and 13 are argument and frame pointer and must not |
43 | | be saved by using the entry mask. */ |
44 | | "~ap~", "~fp~", |
45 | | /* Bits 14 and 15 control integer and decimal overflow. */ |
46 | | "IntOvfl", "DecOvfl", |
47 | | }; |
48 | | |
49 | | /* Sign-extend an (unsigned char). */ |
50 | 736k | #define COERCE_SIGNED_CHAR(ch) ((signed char)(ch)) |
51 | | |
52 | | /* Get a 1 byte signed integer. */ |
53 | | #define NEXTBYTE(p) \ |
54 | 736k | (p += 1, FETCH_DATA (info, p), \ |
55 | 736k | COERCE_SIGNED_CHAR(p[-1])) |
56 | | |
57 | | /* Get a 2 byte signed integer. */ |
58 | 26.0k | #define COERCE16(x) ((int) (((x) ^ 0x8000) - 0x8000)) |
59 | | #define NEXTWORD(p) \ |
60 | 26.0k | (p += 2, FETCH_DATA (info, p), \ |
61 | 26.0k | COERCE16 ((p[-1] << 8) + p[-2])) |
62 | | |
63 | | /* Get a 4 byte signed integer. */ |
64 | 25.1k | #define COERCE32(x) ((int) (((x) ^ 0x80000000) - 0x80000000)) |
65 | | #define NEXTLONG(p) \ |
66 | 25.1k | (p += 4, FETCH_DATA (info, p), \ |
67 | 25.1k | (COERCE32 (((((((unsigned) p[-1] << 8) + p[-2]) << 8) + p[-3]) << 8) + p[-4]))) |
68 | | |
69 | | /* Maximum length of an instruction. */ |
70 | | #define MAXLEN 25 |
71 | | |
72 | | struct private |
73 | | { |
74 | | /* Points to first byte not fetched. */ |
75 | | bfd_byte * max_fetched; |
76 | | bfd_byte the_buffer[MAXLEN]; |
77 | | bfd_vma insn_start; |
78 | | OPCODES_SIGJMP_BUF bailout; |
79 | | }; |
80 | | |
81 | | /* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) |
82 | | to ADDR (exclusive) are valid. Returns 1 for success, longjmps |
83 | | on error. */ |
84 | | #define FETCH_DATA(info, addr) \ |
85 | 2.01M | ((addr) <= ((struct private *)(info->private_data))->max_fetched \ |
86 | 2.01M | ? 1 : fetch_data ((info), (addr))) |
87 | | |
88 | | static int |
89 | | fetch_data (struct disassemble_info *info, bfd_byte *addr) |
90 | 1.13M | { |
91 | 1.13M | int status; |
92 | 1.13M | struct private *priv = (struct private *) info->private_data; |
93 | 1.13M | bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); |
94 | | |
95 | 1.13M | status = (*info->read_memory_func) (start, |
96 | 1.13M | priv->max_fetched, |
97 | 1.13M | addr - priv->max_fetched, |
98 | 1.13M | info); |
99 | 1.13M | if (status != 0) |
100 | 411 | { |
101 | 411 | (*info->memory_error_func) (status, start, info); |
102 | 411 | OPCODES_SIGLONGJMP (priv->bailout, 1); |
103 | 411 | } |
104 | 1.13M | else |
105 | 1.13M | priv->max_fetched = addr; |
106 | | |
107 | 1.13M | return 1; |
108 | 1.13M | } |
109 | | |
110 | | /* Entry mask handling. */ |
111 | | static unsigned int entry_addr_occupied_slots = 0; |
112 | | static unsigned int entry_addr_total_slots = 0; |
113 | | static bfd_vma * entry_addr = NULL; |
114 | | |
115 | | /* Parse the VAX specific disassembler options. These contain function |
116 | | entry addresses, which can be useful to disassemble ROM images, since |
117 | | there's no symbol table. Returns TRUE upon success, FALSE otherwise. */ |
118 | | |
119 | | static bool |
120 | | parse_disassembler_options (const char *options) |
121 | 0 | { |
122 | 0 | const char * entry_switch = "entry:"; |
123 | |
|
124 | 0 | while ((options = strstr (options, entry_switch))) |
125 | 0 | { |
126 | 0 | options += strlen (entry_switch); |
127 | | |
128 | | /* The greater-than part of the test below is paranoia. */ |
129 | 0 | if (entry_addr_occupied_slots >= entry_addr_total_slots) |
130 | 0 | { |
131 | | /* A guesstimate of the number of entries we will have to create. */ |
132 | 0 | entry_addr_total_slots |
133 | 0 | += 1 + strlen (options) / (strlen (entry_switch) + 5); |
134 | |
|
135 | 0 | entry_addr = realloc (entry_addr, sizeof (bfd_vma) |
136 | 0 | * entry_addr_total_slots); |
137 | 0 | } |
138 | |
|
139 | 0 | if (entry_addr == NULL) |
140 | 0 | return false; |
141 | | |
142 | 0 | entry_addr[entry_addr_occupied_slots] = bfd_scan_vma (options, NULL, 0); |
143 | 0 | entry_addr_occupied_slots ++; |
144 | 0 | } |
145 | | |
146 | 0 | return true; |
147 | 0 | } |
148 | | |
149 | | #if 0 /* FIXME: Ideally the disassembler should have target specific |
150 | | initialisation and termination function pointers. Then |
151 | | parse_disassembler_options could be the init function and |
152 | | free_entry_array (below) could be the termination routine. |
153 | | Until then there is no way for the disassembler to tell us |
154 | | that it has finished and that we no longer need the entry |
155 | | array, so this routine is suppressed for now. It does mean |
156 | | that we leak memory, but only to the extent that we do not |
157 | | free it just before the disassembler is about to terminate |
158 | | anyway. */ |
159 | | |
160 | | /* Free memory allocated to our entry array. */ |
161 | | |
162 | | static void |
163 | | free_entry_array (void) |
164 | | { |
165 | | if (entry_addr) |
166 | | { |
167 | | free (entry_addr); |
168 | | entry_addr = NULL; |
169 | | entry_addr_occupied_slots = entry_addr_total_slots = 0; |
170 | | } |
171 | | } |
172 | | #endif |
173 | | /* Check if the given address is a known function entry point. This is |
174 | | the case if there is a symbol of the function type at this address. |
175 | | We also check for synthetic symbols as these are used for PLT entries |
176 | | (weak undefined symbols may not have the function type set). Finally |
177 | | the address may have been forced to be treated as an entry point. The |
178 | | latter helps in disassembling ROM images, because there's no symbol |
179 | | table at all. Forced entry points can be given by supplying several |
180 | | -M options to objdump: -M entry:0xffbb7730. */ |
181 | | |
182 | | static bool |
183 | | is_function_entry (struct disassemble_info *info, bfd_vma addr) |
184 | 627k | { |
185 | 627k | unsigned int i; |
186 | | |
187 | | /* Check if there's a function or PLT symbol at our address. */ |
188 | 627k | if (info->symbols |
189 | 627k | && info->symbols[0] |
190 | 627k | && (info->symbols[0]->flags & (BSF_FUNCTION | BSF_SYNTHETIC)) |
191 | 627k | && addr == bfd_asymbol_value (info->symbols[0])) |
192 | 0 | return true; |
193 | | |
194 | | /* Check for forced function entry address. */ |
195 | 627k | for (i = entry_addr_occupied_slots; i--;) |
196 | 0 | if (entry_addr[i] == addr) |
197 | 0 | return true; |
198 | | |
199 | 627k | return false; |
200 | 627k | } |
201 | | |
202 | | /* Check if the given address is the last longword of a PLT entry. |
203 | | This longword is data and depending on the value it may interfere |
204 | | with disassembly of further PLT entries. We make use of the fact |
205 | | PLT symbols are marked BSF_SYNTHETIC. */ |
206 | | static bool |
207 | | is_plt_tail (struct disassemble_info *info, bfd_vma addr) |
208 | 627k | { |
209 | 627k | if (info->symbols |
210 | 627k | && info->symbols[0] |
211 | 627k | && (info->symbols[0]->flags & BSF_SYNTHETIC) |
212 | 627k | && addr == bfd_asymbol_value (info->symbols[0]) + 8) |
213 | 0 | return true; |
214 | | |
215 | 627k | return false; |
216 | 627k | } |
217 | | |
218 | | static int |
219 | | print_insn_mode (const char *d, |
220 | | int size, |
221 | | unsigned char *p0, |
222 | | bfd_vma addr, /* PC for this arg to be relative to. */ |
223 | | disassemble_info *info) |
224 | 638k | { |
225 | 638k | unsigned char *p = p0; |
226 | 638k | unsigned char mode, reg; |
227 | | |
228 | | /* Fetch and interpret mode byte. */ |
229 | 638k | mode = (unsigned char) NEXTBYTE (p); |
230 | 638k | reg = mode & 0xF; |
231 | 638k | switch (mode & 0xF0) |
232 | 638k | { |
233 | 184k | case 0x00: |
234 | 199k | case 0x10: |
235 | 232k | case 0x20: |
236 | 266k | case 0x30: /* Literal mode $number. */ |
237 | 266k | if (d[1] == 'd' || d[1] == 'f' || d[1] == 'g' || d[1] == 'h') |
238 | 53.2k | (*info->fprintf_func) (info->stream, "$0x%x [%c-float]", mode, d[1]); |
239 | 212k | else |
240 | 212k | (*info->fprintf_func) (info->stream, "$0x%x", mode); |
241 | 266k | break; |
242 | 53.7k | case 0x40: /* Index: base-addr[Rn] */ |
243 | 53.7k | { |
244 | 53.7k | unsigned char *q = p0 + 1; |
245 | 53.7k | unsigned char nextmode = NEXTBYTE (q); |
246 | 53.7k | if (nextmode < 0x60 || nextmode == 0x8f) |
247 | | /* Literal, index, register, or immediate is invalid. In |
248 | | particular don't recurse into another index mode which |
249 | | might overflow the_buffer. */ |
250 | 33.0k | (*info->fprintf_func) (info->stream, "[invalid base]"); |
251 | 20.7k | else |
252 | 20.7k | p += print_insn_mode (d, size, p0 + 1, addr + 1, info); |
253 | 53.7k | (*info->fprintf_func) (info->stream, "[%s]", reg_names[reg]); |
254 | 53.7k | } |
255 | 53.7k | break; |
256 | 42.7k | case 0x50: /* Register: Rn */ |
257 | 42.7k | (*info->fprintf_func) (info->stream, "%s", reg_names[reg]); |
258 | 42.7k | break; |
259 | 118k | case 0x60: /* Register deferred: (Rn) */ |
260 | 118k | (*info->fprintf_func) (info->stream, "(%s)", reg_names[reg]); |
261 | 118k | break; |
262 | 64.7k | case 0x70: /* Autodecrement: -(Rn) */ |
263 | 64.7k | (*info->fprintf_func) (info->stream, "-(%s)", reg_names[reg]); |
264 | 64.7k | break; |
265 | 17.6k | case 0x80: /* Autoincrement: (Rn)+ */ |
266 | 17.6k | if (reg == 0xF) |
267 | 4.38k | { /* Immediate? */ |
268 | 4.38k | int i; |
269 | | |
270 | 4.38k | FETCH_DATA (info, p + size); |
271 | 4.38k | (*info->fprintf_func) (info->stream, "$0x"); |
272 | 4.38k | if (d[1] == 'd' || d[1] == 'f' || d[1] == 'g' || d[1] == 'h') |
273 | 1.50k | { |
274 | 1.50k | int float_word; |
275 | | |
276 | 1.50k | float_word = p[0] | (p[1] << 8); |
277 | 1.50k | if ((d[1] == 'd' || d[1] == 'f') |
278 | 1.50k | && (float_word & 0xff80) == 0x8000) |
279 | 64 | { |
280 | 64 | (*info->fprintf_func) (info->stream, "[invalid %c-float]", |
281 | 64 | d[1]); |
282 | 64 | } |
283 | 1.44k | else |
284 | 1.44k | { |
285 | 16.1k | for (i = 0; i < size; i++) |
286 | 14.6k | (*info->fprintf_func) (info->stream, "%02x", |
287 | 14.6k | p[size - i - 1]); |
288 | 1.44k | (*info->fprintf_func) (info->stream, " [%c-float]", d[1]); |
289 | 1.44k | } |
290 | 1.50k | } |
291 | 2.87k | else |
292 | 2.87k | { |
293 | 7.27k | for (i = 0; i < size; i++) |
294 | 4.39k | (*info->fprintf_func) (info->stream, "%02x", p[size - i - 1]); |
295 | 2.87k | } |
296 | 4.38k | p += size; |
297 | 4.38k | } |
298 | 13.2k | else |
299 | 13.2k | (*info->fprintf_func) (info->stream, "(%s)+", reg_names[reg]); |
300 | 17.6k | break; |
301 | 13.1k | case 0x90: /* Autoincrement deferred: @(Rn)+ */ |
302 | 13.1k | if (reg == 0xF) |
303 | 553 | (*info->fprintf_func) (info->stream, "*0x%x", NEXTLONG (p)); |
304 | 12.6k | else |
305 | 12.6k | (*info->fprintf_func) (info->stream, "@(%s)+", reg_names[reg]); |
306 | 13.1k | break; |
307 | 6.85k | case 0xB0: /* Displacement byte deferred: *displ(Rn). */ |
308 | 6.85k | (*info->fprintf_func) (info->stream, "*"); |
309 | | /* Fall through. */ |
310 | 20.2k | case 0xA0: /* Displacement byte: displ(Rn). */ |
311 | 20.2k | if (reg == 0xF) |
312 | 1.54k | (*info->print_address_func) (addr + 2 + NEXTBYTE (p), info); |
313 | 18.7k | else |
314 | 18.7k | (*info->fprintf_func) (info->stream, "0x%x(%s)", NEXTBYTE (p), |
315 | 18.7k | reg_names[reg]); |
316 | 20.2k | break; |
317 | 7.06k | case 0xD0: /* Displacement word deferred: *displ(Rn). */ |
318 | 7.06k | (*info->fprintf_func) (info->stream, "*"); |
319 | | /* Fall through. */ |
320 | 16.4k | case 0xC0: /* Displacement word: displ(Rn). */ |
321 | 16.4k | if (reg == 0xF) |
322 | 1.64k | (*info->print_address_func) (addr + 3 + NEXTWORD (p), info); |
323 | 14.7k | else |
324 | 14.7k | (*info->fprintf_func) (info->stream, "0x%x(%s)", NEXTWORD (p), |
325 | 14.7k | reg_names[reg]); |
326 | 16.4k | break; |
327 | 13.9k | case 0xF0: /* Displacement long deferred: *displ(Rn). */ |
328 | 13.9k | (*info->fprintf_func) (info->stream, "*"); |
329 | | /* Fall through. */ |
330 | 24.5k | case 0xE0: /* Displacement long: displ(Rn). */ |
331 | 24.5k | if (reg == 0xF) |
332 | 6.67k | (*info->print_address_func) (addr + 5 + NEXTLONG (p), info); |
333 | 17.8k | else |
334 | 17.8k | (*info->fprintf_func) (info->stream, "0x%x(%s)", NEXTLONG (p), |
335 | 17.8k | reg_names[reg]); |
336 | 24.5k | break; |
337 | 638k | } |
338 | | |
339 | 638k | return p - p0; |
340 | 638k | } |
341 | | |
342 | | /* Returns number of bytes "eaten" by the operand, or return -1 if an |
343 | | invalid operand was found, or -2 if an opcode tabel error was |
344 | | found. */ |
345 | | |
346 | | static int |
347 | | print_insn_arg (const char *d, |
348 | | unsigned char *p0, |
349 | | bfd_vma addr, /* PC for this arg to be relative to. */ |
350 | | disassemble_info *info) |
351 | 651k | { |
352 | 651k | int arg_len; |
353 | | |
354 | | /* Check validity of addressing length. */ |
355 | 651k | switch (d[1]) |
356 | 651k | { |
357 | 212k | case 'b' : arg_len = 1; break; |
358 | 130k | case 'd' : arg_len = 8; break; |
359 | 76.9k | case 'f' : arg_len = 4; break; |
360 | 545 | case 'g' : arg_len = 8; break; |
361 | 2.00k | case 'h' : arg_len = 16; break; |
362 | 87.8k | case 'l' : arg_len = 4; break; |
363 | 240 | case 'o' : arg_len = 16; break; |
364 | 127k | case 'w' : arg_len = 2; break; |
365 | 13.0k | case 'q' : arg_len = 8; break; |
366 | 0 | default : abort (); |
367 | 651k | } |
368 | | |
369 | | /* Branches have no mode byte. */ |
370 | 651k | if (d[0] == 'b') |
371 | 33.6k | { |
372 | 33.6k | unsigned char *p = p0; |
373 | | |
374 | 33.6k | if (arg_len == 1) |
375 | 23.9k | (*info->print_address_func) (addr + 1 + NEXTBYTE (p), info); |
376 | 9.67k | else |
377 | 9.67k | (*info->print_address_func) (addr + 2 + NEXTWORD (p), info); |
378 | | |
379 | 33.6k | return p - p0; |
380 | 33.6k | } |
381 | | |
382 | 617k | return print_insn_mode (d, arg_len, p0, addr, info); |
383 | 651k | } |
384 | | |
385 | | /* Print the vax instruction at address MEMADDR in debugged memory, |
386 | | on INFO->STREAM. Returns length of the instruction, in bytes. */ |
387 | | |
388 | | int |
389 | | print_insn_vax (bfd_vma memaddr, disassemble_info *info) |
390 | 627k | { |
391 | 627k | static bool parsed_disassembler_options = false; |
392 | 627k | const struct vot *votp; |
393 | 627k | const char *argp; |
394 | 627k | unsigned char *arg; |
395 | 627k | struct private priv; |
396 | 627k | bfd_byte *buffer = priv.the_buffer; |
397 | | |
398 | 627k | info->private_data = & priv; |
399 | 627k | priv.max_fetched = priv.the_buffer; |
400 | 627k | priv.insn_start = memaddr; |
401 | | |
402 | 627k | if (! parsed_disassembler_options |
403 | 627k | && info->disassembler_options != NULL) |
404 | 0 | { |
405 | 0 | parse_disassembler_options (info->disassembler_options); |
406 | | |
407 | | /* To avoid repeated parsing of these options. */ |
408 | 0 | parsed_disassembler_options = true; |
409 | 0 | } |
410 | | |
411 | 627k | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
412 | | /* Error return. */ |
413 | 411 | return -1; |
414 | | |
415 | 627k | argp = NULL; |
416 | | /* Check if the info buffer has more than one byte left since |
417 | | the last opcode might be a single byte with no argument data. */ |
418 | 627k | if (info->buffer_length - (memaddr - info->buffer_vma) > 1 |
419 | 627k | && (info->stop_vma == 0 || memaddr < (info->stop_vma - 1))) |
420 | 627k | { |
421 | 627k | FETCH_DATA (info, buffer + 2); |
422 | 627k | } |
423 | 18.4E | else |
424 | 18.4E | { |
425 | 18.4E | FETCH_DATA (info, buffer + 1); |
426 | 18.4E | buffer[1] = 0; |
427 | 18.4E | } |
428 | | |
429 | | /* Decode function entry mask. */ |
430 | 627k | if (is_function_entry (info, memaddr)) |
431 | 0 | { |
432 | 0 | int i = 0; |
433 | 0 | int register_mask = buffer[1] << 8 | buffer[0]; |
434 | |
|
435 | 0 | (*info->fprintf_func) (info->stream, ".word 0x%04x # Entry mask: <", |
436 | 0 | register_mask); |
437 | |
|
438 | 0 | for (i = 15; i >= 0; i--) |
439 | 0 | if (register_mask & (1 << i)) |
440 | 0 | (*info->fprintf_func) (info->stream, " %s", entry_mask_bit[i]); |
441 | |
|
442 | 0 | (*info->fprintf_func) (info->stream, " >"); |
443 | |
|
444 | 0 | return 2; |
445 | 0 | } |
446 | | |
447 | | /* Decode PLT entry offset longword. */ |
448 | 627k | if (is_plt_tail (info, memaddr)) |
449 | 0 | { |
450 | 0 | int offset; |
451 | |
|
452 | 0 | FETCH_DATA (info, buffer + 4); |
453 | 0 | offset = ((unsigned) buffer[3] << 24 | buffer[2] << 16 |
454 | 0 | | buffer[1] << 8 | buffer[0]); |
455 | 0 | (*info->fprintf_func) (info->stream, ".long 0x%08x", offset); |
456 | |
|
457 | 0 | return 4; |
458 | 0 | } |
459 | | |
460 | 37.0M | for (votp = &votstrs[0]; votp->name[0]; votp++) |
461 | 36.9M | { |
462 | 36.9M | vax_opcodeT opcode = votp->detail.code; |
463 | | |
464 | | /* 2 byte codes match 2 buffer pos. */ |
465 | 36.9M | if ((bfd_byte) opcode == buffer[0] |
466 | 36.9M | && (opcode >> 8 == 0 || opcode >> 8 == buffer[1])) |
467 | 592k | { |
468 | 592k | argp = votp->detail.args; |
469 | 592k | break; |
470 | 592k | } |
471 | 36.9M | } |
472 | 627k | if (argp == NULL) |
473 | 35.1k | { |
474 | | /* Handle undefined instructions. */ |
475 | 35.1k | (*info->fprintf_func) (info->stream, ".word 0x%x", |
476 | 35.1k | (buffer[0] << 8) + buffer[1]); |
477 | 35.1k | return 2; |
478 | 35.1k | } |
479 | | |
480 | | /* Point at first byte of argument data, and at descriptor for first |
481 | | argument. */ |
482 | 591k | arg = buffer + ((votp->detail.code >> 8) ? 2 : 1); |
483 | | |
484 | | /* Make sure we have it in mem */ |
485 | 591k | FETCH_DATA (info, arg); |
486 | | |
487 | 591k | (*info->fprintf_func) (info->stream, "%s", votp->name); |
488 | 591k | if (*argp) |
489 | 248k | (*info->fprintf_func) (info->stream, " "); |
490 | | |
491 | 1.24M | while (*argp) |
492 | 651k | { |
493 | 651k | arg += print_insn_arg (argp, arg, memaddr + (arg - buffer), info); |
494 | 651k | argp += 2; |
495 | 651k | if (*argp) |
496 | 402k | (*info->fprintf_func) (info->stream, ","); |
497 | 651k | } |
498 | | |
499 | 591k | return arg - buffer; |
500 | 627k | } |
501 | | |