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