S/390: Dump unknown instructions according to their length.
authorAndreas Krebbel <krebbel@linux.vnet.ibm.com>
Fri, 10 Jun 2016 11:40:48 +0000 (13:40 +0200)
committerAndreas Krebbel <krebbel@linux.vnet.ibm.com>
Fri, 10 Jun 2016 11:41:42 +0000 (13:41 +0200)
Unknown instructions are currently just dumped as .long 1234.  On
S/390 we can do a bit better since the instruction length is encoded
in the opcode.  That way also unknown instructions can be skipped
according to their real length.  That way we can continue correctly
after that instruction.  However, there are also some drawbacks with
that behavior when dumping data.  So for now that behavior is only
enabled for text section but even there it might mess things up when
having a literal pool embedded in the code.  Therefore I've left the
feature disabled by default and have added the -Minsnlength option to
enable it explicitely.

opcodes/ChangeLog:

2016-06-10  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>

* s390-dis.c (option_use_insn_len_bits_p): New file scope
variable.
(init_disasm): Handle new command line option "insnlength".
(print_s390_disassembler_options): Mention new option in help
output.
(print_insn_s390): Use the encoded insn length when dumping
unknown instructions.

opcodes/ChangeLog
opcodes/s390-dis.c

index f218365..fbde3e1 100644 (file)
@@ -1,3 +1,13 @@
+2016-06-10  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>
+
+       * s390-dis.c (option_use_insn_len_bits_p): New file scope
+       variable.
+       (init_disasm): Handle new command line option "insnlength".
+       (print_s390_disassembler_options): Mention new option in help
+       output.
+       (print_insn_s390): Use the encoded insn length when dumping
+       unknown instructions.
+
 2016-06-03  Pitchumani Sivanupandi  <pitchumani.s@atmel.com>
 
        * avr-dis.c (avr_operand): Add default data address space origin (0x800000)
index eeaeaf8..8134073 100644 (file)
@@ -29,6 +29,7 @@
 static int init_flag = 0;
 static int opc_index[256];
 static int current_arch_mask = 0;
+static int option_use_insn_len_bits_p = 0;
 
 /* Set up index table for first opcode byte.  */
 
@@ -51,6 +52,8 @@ init_disasm (struct disassemble_info *info)
        current_arch_mask = 1 << S390_OPCODE_ESA;
       else if (CONST_STRNEQ (p, "zarch"))
        current_arch_mask = 1 << S390_OPCODE_ZARCH;
+      else if (CONST_STRNEQ (p, "insnlength"))
+       option_use_insn_len_bits_p = 1;
       else
        fprintf (stderr, "Unknown S/390 disassembler option: %s\n", p);
 
@@ -261,7 +264,7 @@ print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
   bfd_byte buffer[6];
   const struct s390_opcode *opcode = NULL;
   unsigned int value;
-  int status, opsize, bufsize;
+  int status, opsize, bufsize, bytes_to_dump, i;
 
   if (init_flag == 0)
     init_disasm (info);
@@ -307,38 +310,54 @@ print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
                  || opcode_mask_more_specific (op, opcode)))
            opcode = op;
        }
-    }
 
-  if (opcode != NULL)
-    {
-      /* The instruction is valid.  Print it and return its size.  */
-      s390_print_insn_with_opcode (memaddr, info, buffer, opcode);
-      return opsize;
+      if (opcode != NULL)
+       {
+         /* The instruction is valid.  Print it and return its size.  */
+         s390_print_insn_with_opcode (memaddr, info, buffer, opcode);
+         return opsize;
+       }
     }
 
+  /* For code sections it makes sense to skip unknown instructions
+     according to their length bits.  */
+  if (status == 0
+      && option_use_insn_len_bits_p
+      && info->section != NULL
+      && (info->section->flags & SEC_CODE))
+    bytes_to_dump = opsize;
+  else
+    /* By default unknown instructions are printed as .long's/.short'
+       depending on how many bytes are available.  */
+    bytes_to_dump = bufsize >= 4 ? 4 : bufsize;
+
+  if (bytes_to_dump == 0)
+    return 0;
+
   /* Fall back to hex print.  */
-  if (bufsize >= 4)
+  switch (bytes_to_dump)
     {
+    case 4:
       value = (unsigned int) buffer[0];
       value = (value << 8) + (unsigned int) buffer[1];
       value = (value << 8) + (unsigned int) buffer[2];
       value = (value << 8) + (unsigned int) buffer[3];
       info->fprintf_func (info->stream, ".long\t0x%08x", value);
       return 4;
-    }
-  else if (bufsize >= 2)
-    {
+    case 2:
       value = (unsigned int) buffer[0];
       value = (value << 8) + (unsigned int) buffer[1];
       info->fprintf_func (info->stream, ".short\t0x%04x", value);
       return 2;
+    default:
+      info->fprintf_func (info->stream, ".byte\t0x%02x",
+                         (unsigned int) buffer[0]);
+      for (i = 1; i < bytes_to_dump; i++)
+       info->fprintf_func (info->stream, ",0x%02x",
+                         (unsigned int) buffer[i]);
+      return bytes_to_dump;
     }
-  else
-    {
-      value = (unsigned int) buffer[0];
-      info->fprintf_func (info->stream, ".byte\t0x%02x", value);
-      return 1;
-    }
+  return 0;
 }
 
 void
@@ -350,4 +369,6 @@ with the -M switch (multiple options should be separated by commas):\n"));
 
   fprintf (stream, _("  esa         Disassemble in ESA architecture mode\n"));
   fprintf (stream, _("  zarch       Disassemble in z/Architecture mode\n"));
+  fprintf (stream, _("  insnlength  Print unknown instructions according "
+                    "to length from first two bits\n"));
 }