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