/src/binutils-gdb/opcodes/s390-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* s390-dis.c -- Disassemble S390 instructions |
2 | | Copyright (C) 2000-2023 Free Software Foundation, Inc. |
3 | | Contributed by Martin Schwidefsky (schwidefsky@de.ibm.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 <stdio.h> |
24 | | #include "ansidecl.h" |
25 | | #include "disassemble.h" |
26 | | #include "opintl.h" |
27 | | #include "opcode/s390.h" |
28 | | #include "libiberty.h" |
29 | | |
30 | | static int opc_index[256]; |
31 | | static int current_arch_mask = 0; |
32 | | static int option_use_insn_len_bits_p = 0; |
33 | | |
34 | | typedef struct |
35 | | { |
36 | | const char *name; |
37 | | const char *description; |
38 | | } s390_options_t; |
39 | | |
40 | | static const s390_options_t options[] = |
41 | | { |
42 | | { "esa" , N_("Disassemble in ESA architecture mode") }, |
43 | | { "zarch", N_("Disassemble in z/Architecture mode") }, |
44 | | { "insnlength", N_("Print unknown instructions according to " |
45 | | "length from first two bits") } |
46 | | }; |
47 | | |
48 | | /* Set up index table for first opcode byte. */ |
49 | | |
50 | | void |
51 | | disassemble_init_s390 (struct disassemble_info *info) |
52 | 66 | { |
53 | 66 | int i; |
54 | 66 | const char *p; |
55 | | |
56 | 66 | memset (opc_index, 0, sizeof (opc_index)); |
57 | | |
58 | | /* Reverse order, such that each opc_index ends up pointing to the |
59 | | first matching entry instead of the last. */ |
60 | 174k | for (i = s390_num_opcodes; i--; ) |
61 | 174k | opc_index[s390_opcodes[i].opcode[0]] = i; |
62 | | |
63 | 66 | current_arch_mask = 1 << S390_OPCODE_ZARCH; |
64 | 66 | option_use_insn_len_bits_p = 0; |
65 | | |
66 | 66 | for (p = info->disassembler_options; p != NULL; ) |
67 | 0 | { |
68 | 0 | if (startswith (p, "esa")) |
69 | 0 | current_arch_mask = 1 << S390_OPCODE_ESA; |
70 | 0 | else if (startswith (p, "zarch")) |
71 | 0 | current_arch_mask = 1 << S390_OPCODE_ZARCH; |
72 | 0 | else if (startswith (p, "insnlength")) |
73 | 0 | option_use_insn_len_bits_p = 1; |
74 | 0 | else |
75 | | /* xgettext:c-format */ |
76 | 0 | opcodes_error_handler (_("unknown S/390 disassembler option: %s"), p); |
77 | |
|
78 | 0 | p = strchr (p, ','); |
79 | 0 | if (p != NULL) |
80 | 0 | p++; |
81 | 0 | } |
82 | 66 | } |
83 | | |
84 | | /* Derive the length of an instruction from its first byte. */ |
85 | | |
86 | | static inline int |
87 | | s390_insn_length (const bfd_byte *buffer) |
88 | 212k | { |
89 | | /* 00xxxxxx -> 2, 01xxxxxx/10xxxxxx -> 4, 11xxxxxx -> 6. */ |
90 | 212k | return ((buffer[0] >> 6) + 3) & ~1U; |
91 | 212k | } |
92 | | |
93 | | /* Match the instruction in BUFFER against the given OPCODE, excluding |
94 | | the first byte. */ |
95 | | |
96 | | static inline int |
97 | | s390_insn_matches_opcode (const bfd_byte *buffer, |
98 | | const struct s390_opcode *opcode) |
99 | 12.9M | { |
100 | 12.9M | return (buffer[1] & opcode->mask[1]) == opcode->opcode[1] |
101 | 12.9M | && (buffer[2] & opcode->mask[2]) == opcode->opcode[2] |
102 | 12.9M | && (buffer[3] & opcode->mask[3]) == opcode->opcode[3] |
103 | 12.9M | && (buffer[4] & opcode->mask[4]) == opcode->opcode[4] |
104 | 12.9M | && (buffer[5] & opcode->mask[5]) == opcode->opcode[5]; |
105 | 12.9M | } |
106 | | |
107 | | union operand_value |
108 | | { |
109 | | int i; |
110 | | unsigned int u; |
111 | | }; |
112 | | |
113 | | /* Extracts an operand value from an instruction. */ |
114 | | /* We do not perform the shift operation for larl-type address |
115 | | operands here since that would lead to an overflow of the 32 bit |
116 | | integer value. Instead the shift operation is done when printing |
117 | | the operand. */ |
118 | | |
119 | | static inline union operand_value |
120 | | s390_extract_operand (const bfd_byte *insn, |
121 | | const struct s390_operand *operand) |
122 | 392k | { |
123 | 392k | union operand_value ret; |
124 | 392k | unsigned int val; |
125 | 392k | int bits; |
126 | 392k | const bfd_byte *orig_insn = insn; |
127 | | |
128 | | /* Extract fragments of the operand byte for byte. */ |
129 | 392k | insn += operand->shift / 8; |
130 | 392k | bits = (operand->shift & 7) + operand->bits; |
131 | 392k | val = 0; |
132 | 392k | do |
133 | 548k | { |
134 | 548k | val <<= 8; |
135 | 548k | val |= (unsigned int) *insn++; |
136 | 548k | bits -= 8; |
137 | 548k | } |
138 | 548k | while (bits > 0); |
139 | 392k | val >>= -bits; |
140 | 392k | val &= ((1U << (operand->bits - 1)) << 1) - 1; |
141 | | |
142 | | /* Check for special long displacement case. */ |
143 | 392k | if (operand->bits == 20 && operand->shift == 20) |
144 | 68 | val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; |
145 | | |
146 | | /* Sign extend value if the operand is signed or pc relative. Avoid |
147 | | integer overflows. */ |
148 | 392k | if (operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL)) |
149 | 56.4k | { |
150 | 56.4k | unsigned int m = 1U << (operand->bits - 1); |
151 | | |
152 | 56.4k | if (val >= m) |
153 | 20.4k | ret.i = (int) (val - m) - 1 - (int) (m - 1U); |
154 | 35.9k | else |
155 | 35.9k | ret.i = (int) val; |
156 | 56.4k | } |
157 | 336k | else if (operand->flags & S390_OPERAND_LENGTH) |
158 | | /* Length x in an instruction has real length x + 1. */ |
159 | 3.18k | ret.u = val + 1; |
160 | | |
161 | 333k | else if (operand->flags & S390_OPERAND_VR) |
162 | 31 | { |
163 | | /* Extract the extra bits for a vector register operand stored |
164 | | in the RXB field. */ |
165 | 31 | unsigned vr = operand->shift == 32 ? 3 |
166 | 31 | : (unsigned) operand->shift / 4 - 2; |
167 | | |
168 | 31 | ret.u = val | ((orig_insn[4] & (1 << (3 - vr))) << (vr + 1)); |
169 | 31 | } |
170 | 333k | else |
171 | 333k | ret.u = val; |
172 | | |
173 | 392k | return ret; |
174 | 392k | } |
175 | | |
176 | | /* Print the S390 instruction in BUFFER, assuming that it matches the |
177 | | given OPCODE. */ |
178 | | |
179 | | static void |
180 | | s390_print_insn_with_opcode (bfd_vma memaddr, |
181 | | struct disassemble_info *info, |
182 | | const bfd_byte *buffer, |
183 | | const struct s390_opcode *opcode) |
184 | 154k | { |
185 | 154k | const unsigned char *opindex; |
186 | 154k | char separator; |
187 | | |
188 | | /* Mnemonic. */ |
189 | 154k | info->fprintf_styled_func (info->stream, dis_style_mnemonic, |
190 | 154k | "%s", opcode->name); |
191 | | |
192 | | /* Operands. */ |
193 | 154k | separator = '\t'; |
194 | 547k | for (opindex = opcode->operands; *opindex != 0; opindex++) |
195 | 392k | { |
196 | 392k | const struct s390_operand *operand = s390_operands + *opindex; |
197 | 392k | union operand_value val = s390_extract_operand (buffer, operand); |
198 | 392k | unsigned long flags = operand->flags; |
199 | | |
200 | 392k | if ((flags & S390_OPERAND_INDEX) && val.u == 0) |
201 | 25.9k | continue; |
202 | 366k | if ((flags & S390_OPERAND_BASE) && |
203 | 366k | val.u == 0 && separator == '(') |
204 | 4.45k | { |
205 | 4.45k | separator = ','; |
206 | 4.45k | continue; |
207 | 4.45k | } |
208 | | |
209 | | /* For instructions with a last optional operand don't print it |
210 | | if zero. */ |
211 | 362k | if ((opcode->flags & (S390_INSTR_FLAG_OPTPARM | S390_INSTR_FLAG_OPTPARM2)) |
212 | 362k | && val.u == 0 |
213 | 362k | && opindex[1] == 0) |
214 | 10 | break; |
215 | | |
216 | 362k | if ((opcode->flags & S390_INSTR_FLAG_OPTPARM2) |
217 | 362k | && val.u == 0 && opindex[1] != 0 && opindex[2] == 0) |
218 | 0 | { |
219 | 0 | union operand_value next_op_val = |
220 | 0 | s390_extract_operand (buffer, s390_operands + opindex[1]); |
221 | 0 | if (next_op_val.u == 0) |
222 | 0 | break; |
223 | 0 | } |
224 | | |
225 | 362k | if (flags & S390_OPERAND_GPR) |
226 | 208k | { |
227 | 208k | info->fprintf_styled_func (info->stream, dis_style_text, |
228 | 208k | "%c", separator); |
229 | 208k | info->fprintf_styled_func (info->stream, dis_style_register, |
230 | 208k | "%%r%u", val.u); |
231 | 208k | } |
232 | 154k | else if (flags & S390_OPERAND_FPR) |
233 | 27.1k | { |
234 | 27.1k | info->fprintf_styled_func (info->stream, dis_style_text, |
235 | 27.1k | "%c", separator); |
236 | 27.1k | info->fprintf_styled_func (info->stream, dis_style_register, |
237 | 27.1k | "%%f%u", val.u); |
238 | 27.1k | } |
239 | 127k | else if (flags & S390_OPERAND_VR) |
240 | 31 | { |
241 | 31 | info->fprintf_styled_func (info->stream, dis_style_text, |
242 | 31 | "%c", separator); |
243 | 31 | info->fprintf_styled_func (info->stream, dis_style_register, |
244 | 31 | "%%v%i", val.u); |
245 | 31 | } |
246 | 126k | else if (flags & S390_OPERAND_AR) |
247 | 172 | { |
248 | 172 | info->fprintf_styled_func (info->stream, dis_style_text, |
249 | 172 | "%c", separator); |
250 | 172 | info->fprintf_styled_func (info->stream, dis_style_register, |
251 | 172 | "%%a%u", val.u); |
252 | 172 | } |
253 | 126k | else if (flags & S390_OPERAND_CR) |
254 | 142 | { |
255 | 142 | info->fprintf_styled_func (info->stream, dis_style_text, |
256 | 142 | "%c", separator); |
257 | 142 | info->fprintf_styled_func (info->stream, dis_style_register, |
258 | 142 | "%%c%u", val.u); |
259 | 142 | } |
260 | 126k | else if (flags & S390_OPERAND_PCREL) |
261 | 38.5k | { |
262 | 38.5k | info->fprintf_styled_func (info->stream, dis_style_text, |
263 | 38.5k | "%c", separator); |
264 | 38.5k | info->print_address_func (memaddr + val.i + val.i, info); |
265 | 38.5k | } |
266 | 88.0k | else if (flags & S390_OPERAND_SIGNED) |
267 | 17.8k | { |
268 | 17.8k | enum disassembler_style style; |
269 | | |
270 | 17.8k | info->fprintf_styled_func (info->stream, dis_style_text, |
271 | 17.8k | "%c", separator); |
272 | 17.8k | style = ((flags & S390_OPERAND_DISP) |
273 | 17.8k | ? dis_style_address_offset : dis_style_immediate); |
274 | 17.8k | info->fprintf_styled_func (info->stream, style, "%i", val.i); |
275 | 17.8k | } |
276 | 70.2k | else |
277 | 70.2k | { |
278 | 70.2k | enum disassembler_style style; |
279 | | |
280 | 70.2k | if (flags & S390_OPERAND_OR1) |
281 | 0 | val.u &= ~1; |
282 | 70.2k | if (flags & S390_OPERAND_OR2) |
283 | 0 | val.u &= ~2; |
284 | 70.2k | if (flags & S390_OPERAND_OR8) |
285 | 0 | val.u &= ~8; |
286 | | |
287 | 70.2k | if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) |
288 | 70.2k | && val.u == 0 |
289 | 70.2k | && opindex[1] == 0) |
290 | 0 | break; |
291 | 70.2k | info->fprintf_styled_func (info->stream, dis_style_text, |
292 | 70.2k | "%c", separator); |
293 | 70.2k | style = ((flags & S390_OPERAND_DISP) |
294 | 70.2k | ? dis_style_address_offset : dis_style_immediate); |
295 | 70.2k | info->fprintf_styled_func (info->stream, style, "%u", val.u); |
296 | 70.2k | } |
297 | | |
298 | 362k | if (flags & S390_OPERAND_DISP) |
299 | 60.3k | separator = '('; |
300 | 301k | else if (flags & S390_OPERAND_BASE) |
301 | 55.9k | { |
302 | 55.9k | info->fprintf_styled_func (info->stream, dis_style_text, ")"); |
303 | 55.9k | separator = ','; |
304 | 55.9k | } |
305 | 246k | else |
306 | 246k | separator = ','; |
307 | 362k | } |
308 | 154k | } |
309 | | |
310 | | /* Check whether opcode A's mask is more specific than that of B. */ |
311 | | |
312 | | static int |
313 | | opcode_mask_more_specific (const struct s390_opcode *a, |
314 | | const struct s390_opcode *b) |
315 | 99.5k | { |
316 | 99.5k | return (((int) a->mask[0] + a->mask[1] + a->mask[2] |
317 | 99.5k | + a->mask[3] + a->mask[4] + a->mask[5]) |
318 | 99.5k | > ((int) b->mask[0] + b->mask[1] + b->mask[2] |
319 | 99.5k | + b->mask[3] + b->mask[4] + b->mask[5])); |
320 | 99.5k | } |
321 | | |
322 | | /* Print a S390 instruction. */ |
323 | | |
324 | | int |
325 | | print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info) |
326 | 212k | { |
327 | 212k | bfd_byte buffer[6]; |
328 | 212k | const struct s390_opcode *opcode = NULL; |
329 | 212k | unsigned int value; |
330 | 212k | int status, opsize, bufsize, bytes_to_dump, i; |
331 | | |
332 | | /* The output looks better if we put 6 bytes on a line. */ |
333 | 212k | info->bytes_per_line = 6; |
334 | | |
335 | | /* Every S390 instruction is max 6 bytes long. */ |
336 | 212k | memset (buffer, 0, 6); |
337 | 212k | status = info->read_memory_func (memaddr, buffer, 6, info); |
338 | 212k | if (status != 0) |
339 | 122 | { |
340 | 528 | for (bufsize = 0; bufsize < 6; bufsize++) |
341 | 528 | if (info->read_memory_func (memaddr, buffer, bufsize + 1, info) != 0) |
342 | 122 | break; |
343 | 122 | if (bufsize <= 0) |
344 | 0 | { |
345 | 0 | info->memory_error_func (status, memaddr, info); |
346 | 0 | return -1; |
347 | 0 | } |
348 | 122 | opsize = s390_insn_length (buffer); |
349 | 122 | status = opsize > bufsize; |
350 | 122 | } |
351 | 212k | else |
352 | 212k | { |
353 | 212k | bufsize = 6; |
354 | 212k | opsize = s390_insn_length (buffer); |
355 | 212k | } |
356 | | |
357 | 212k | if (status == 0) |
358 | 212k | { |
359 | 212k | const struct s390_opcode *op; |
360 | | |
361 | | /* Find the "best match" in the opcode table. */ |
362 | 212k | for (op = s390_opcodes + opc_index[buffer[0]]; |
363 | 13.2M | op != s390_opcodes + s390_num_opcodes |
364 | 13.2M | && op->opcode[0] == buffer[0]; |
365 | 12.9M | op++) |
366 | 12.9M | { |
367 | 12.9M | if ((op->modes & current_arch_mask) |
368 | 12.9M | && s390_insn_matches_opcode (buffer, op) |
369 | 12.9M | && (opcode == NULL |
370 | 253k | || opcode_mask_more_specific (op, opcode))) |
371 | 174k | opcode = op; |
372 | 12.9M | } |
373 | | |
374 | 212k | if (opcode != NULL) |
375 | 154k | { |
376 | | /* The instruction is valid. Print it and return its size. */ |
377 | 154k | s390_print_insn_with_opcode (memaddr, info, buffer, opcode); |
378 | 154k | return opsize; |
379 | 154k | } |
380 | 212k | } |
381 | | |
382 | | /* For code sections it makes sense to skip unknown instructions |
383 | | according to their length bits. */ |
384 | 57.9k | if (status == 0 |
385 | 57.9k | && option_use_insn_len_bits_p |
386 | 57.9k | && info->section != NULL |
387 | 57.9k | && (info->section->flags & SEC_CODE)) |
388 | 0 | bytes_to_dump = opsize; |
389 | 57.9k | else |
390 | | /* By default unknown instructions are printed as .long's/.short' |
391 | | depending on how many bytes are available. */ |
392 | 57.9k | bytes_to_dump = bufsize >= 4 ? 4 : bufsize; |
393 | | |
394 | 57.9k | if (bytes_to_dump == 0) |
395 | 0 | return 0; |
396 | | |
397 | | /* Fall back to hex print. */ |
398 | 57.9k | switch (bytes_to_dump) |
399 | 57.9k | { |
400 | 57.8k | case 4: |
401 | 57.8k | value = (unsigned int) buffer[0]; |
402 | 57.8k | value = (value << 8) + (unsigned int) buffer[1]; |
403 | 57.8k | value = (value << 8) + (unsigned int) buffer[2]; |
404 | 57.8k | value = (value << 8) + (unsigned int) buffer[3]; |
405 | 57.8k | info->fprintf_styled_func (info->stream, dis_style_assembler_directive, |
406 | 57.8k | ".long"); |
407 | 57.8k | info->fprintf_styled_func (info->stream, dis_style_text, |
408 | 57.8k | "\t"); |
409 | 57.8k | info->fprintf_styled_func (info->stream, dis_style_immediate, |
410 | 57.8k | "0x%08x", value); |
411 | 57.8k | return 4; |
412 | 8 | case 2: |
413 | 8 | value = (unsigned int) buffer[0]; |
414 | 8 | value = (value << 8) + (unsigned int) buffer[1]; |
415 | 8 | info->fprintf_styled_func (info->stream, dis_style_assembler_directive, |
416 | 8 | ".short"); |
417 | 8 | info->fprintf_styled_func (info->stream, dis_style_text, |
418 | 8 | "\t"); |
419 | 8 | info->fprintf_styled_func (info->stream, dis_style_immediate, |
420 | 8 | "0x%04x", value); |
421 | 8 | return 2; |
422 | 47 | default: |
423 | 47 | info->fprintf_styled_func (info->stream, dis_style_assembler_directive, |
424 | 47 | ".byte"); |
425 | 47 | info->fprintf_styled_func (info->stream, dis_style_text, |
426 | 47 | "\t"); |
427 | 47 | info->fprintf_styled_func (info->stream, dis_style_immediate, |
428 | 47 | "0x%02x", (unsigned int) buffer[0]); |
429 | 105 | for (i = 1; i < bytes_to_dump; i++) |
430 | 58 | info->fprintf_styled_func (info->stream, dis_style_immediate, |
431 | 58 | "0x%02x", (unsigned int) buffer[i]); |
432 | 47 | return bytes_to_dump; |
433 | 57.9k | } |
434 | 0 | return 0; |
435 | 57.9k | } |
436 | | |
437 | | const disasm_options_and_args_t * |
438 | | disassembler_options_s390 (void) |
439 | 0 | { |
440 | 0 | static disasm_options_and_args_t *opts_and_args; |
441 | |
|
442 | 0 | if (opts_and_args == NULL) |
443 | 0 | { |
444 | 0 | size_t i, num_options = ARRAY_SIZE (options); |
445 | 0 | disasm_options_t *opts; |
446 | |
|
447 | 0 | opts_and_args = XNEW (disasm_options_and_args_t); |
448 | 0 | opts_and_args->args = NULL; |
449 | |
|
450 | 0 | opts = &opts_and_args->options; |
451 | 0 | opts->name = XNEWVEC (const char *, num_options + 1); |
452 | 0 | opts->description = XNEWVEC (const char *, num_options + 1); |
453 | 0 | opts->arg = NULL; |
454 | 0 | for (i = 0; i < num_options; i++) |
455 | 0 | { |
456 | 0 | opts->name[i] = options[i].name; |
457 | 0 | opts->description[i] = _(options[i].description); |
458 | 0 | } |
459 | | /* The array we return must be NULL terminated. */ |
460 | 0 | opts->name[i] = NULL; |
461 | 0 | opts->description[i] = NULL; |
462 | 0 | } |
463 | |
|
464 | 0 | return opts_and_args; |
465 | 0 | } |
466 | | |
467 | | void |
468 | | print_s390_disassembler_options (FILE *stream) |
469 | 0 | { |
470 | 0 | unsigned int i, max_len = 0; |
471 | 0 | fprintf (stream, _("\n\ |
472 | 0 | The following S/390 specific disassembler options are supported for use\n\ |
473 | 0 | with the -M switch (multiple options should be separated by commas):\n")); |
474 | |
|
475 | 0 | for (i = 0; i < ARRAY_SIZE (options); i++) |
476 | 0 | { |
477 | 0 | unsigned int len = strlen (options[i].name); |
478 | 0 | if (max_len < len) |
479 | 0 | max_len = len; |
480 | 0 | } |
481 | |
|
482 | 0 | for (i = 0, max_len++; i < ARRAY_SIZE (options); i++) |
483 | 0 | fprintf (stream, " %s%*c %s\n", |
484 | 0 | options[i].name, |
485 | 0 | (int)(max_len - strlen (options[i].name)), ' ', |
486 | 0 | _(options[i].description)); |
487 | 0 | } |