opcodes: blackfin: fix decoding of all register move insns
[binutils-gdb.git] / opcodes / arm-dis.c
index e246fe9f50e15f88339e6e67ca1cdaa04eda1ec4..831b26c909da04c261c6b416422fb331e67595bf 100644 (file)
@@ -1,6 +1,6 @@
 /* Instruction printing code for the ARM
    Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
    Modification by James G. Smith (jsmith@cygnus.co.uk)
 
 #define NUM_ELEM(a)     (sizeof (a) / sizeof (a)[0])
 #endif
 
+struct arm_private_data
+{
+  /* The features to use when disassembling optional instructions.  */
+  arm_feature_set features;
+
+  /* Whether any mapping symbols are present in the provided symbol
+     table.  -1 if we do not know yet, otherwise 0 or 1.  */
+  int has_mapping_symbols;
+};
+
 struct opcode32
 {
   unsigned long arch;          /* Architecture defining this insn.  */
@@ -464,6 +474,7 @@ static const struct opcode32 coprocessor_opcodes[] =
   {ARM_EXT_V5E, 0x0c400000, 0x0ff00000, "mcrr%c\t%8-11d, %4-7d, %12-15R, %16-19r, cr%0-3d"},
   {ARM_EXT_V5E, 0x0c500000, 0x0ff00000, "mrrc%c\t%8-11d, %4-7d, %12-15Ru, %16-19Ru, cr%0-3d"},
   {ARM_EXT_V2, 0x0e000000, 0x0f000010, "cdp%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+  {ARM_EXT_V2, 0x0e10f010, 0x0f10f010, "mrc%c\t%8-11d, %21-23d, APSR_nzcv, cr%16-19d, cr%0-3d, {%5-7d}"},
   {ARM_EXT_V2, 0x0e100010, 0x0f100010, "mrc%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
   {ARM_EXT_V2, 0x0e000010, 0x0f100010, "mcr%c\t%8-11d, %21-23d, %12-15R, cr%16-19d, cr%0-3d, {%5-7d}"},
   {ARM_EXT_V2, 0x0c000000, 0x0e100000, "stc%22'l%c\t%8-11d, cr%12-15d, %A"},
@@ -871,40 +882,40 @@ static const struct opcode32 arm_opcodes[] =
   {ARM_EXT_V6, 0x01900f9f, 0x0ff00fff, "ldrex%c\tr%12-15d, [%16-19R]"},
   {ARM_EXT_V6, 0x06200f10, 0x0ff00ff0, "qadd16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06200f90, 0x0ff00ff0, "qadd8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06200f30, 0x0ff00ff0, "qaddsubx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06200f30, 0x0ff00ff0, "qasx%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06200f70, 0x0ff00ff0, "qsub16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06200ff0, 0x0ff00ff0, "qsub8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06200f50, 0x0ff00ff0, "qsubaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06200f50, 0x0ff00ff0, "qsax%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06100f10, 0x0ff00ff0, "sadd16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06100f90, 0x0ff00ff0, "sadd8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06100f30, 0x0ff00ff0, "saddaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06100f30, 0x0ff00ff0, "sasx%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06300f10, 0x0ff00ff0, "shadd16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06300f90, 0x0ff00ff0, "shadd8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06300f30, 0x0ff00ff0, "shaddsubx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06300f30, 0x0ff00ff0, "shasx%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06300f70, 0x0ff00ff0, "shsub16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06300ff0, 0x0ff00ff0, "shsub8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06300f50, 0x0ff00ff0, "shsubaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06300f50, 0x0ff00ff0, "shsax%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06100f70, 0x0ff00ff0, "ssub16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06100ff0, 0x0ff00ff0, "ssub8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06100f50, 0x0ff00ff0, "ssubaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06100f50, 0x0ff00ff0, "ssax%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06500f10, 0x0ff00ff0, "uadd16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06500f90, 0x0ff00ff0, "uadd8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06500f30, 0x0ff00ff0, "uaddsubx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06500f30, 0x0ff00ff0, "uasx%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06700f10, 0x0ff00ff0, "uhadd16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06700f90, 0x0ff00ff0, "uhadd8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06700f30, 0x0ff00ff0, "uhaddsubx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06700f30, 0x0ff00ff0, "uhasx%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06700f70, 0x0ff00ff0, "uhsub16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06700ff0, 0x0ff00ff0, "uhsub8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06700f50, 0x0ff00ff0, "uhsubaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06700f50, 0x0ff00ff0, "uhsax%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06600f10, 0x0ff00ff0, "uqadd16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06600f90, 0x0ff00ff0, "uqadd8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06600f30, 0x0ff00ff0, "uqaddsubx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06600f30, 0x0ff00ff0, "uqasx%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06600f70, 0x0ff00ff0, "uqsub16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06600ff0, 0x0ff00ff0, "uqsub8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06600f50, 0x0ff00ff0, "uqsubaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06600f50, 0x0ff00ff0, "uqsax%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06500f70, 0x0ff00ff0, "usub16%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06500ff0, 0x0ff00ff0, "usub8%c\t%12-15R, %16-19R, %0-3R"},
-  {ARM_EXT_V6, 0x06500f50, 0x0ff00ff0, "usubaddx%c\t%12-15R, %16-19R, %0-3R"},
+  {ARM_EXT_V6, 0x06500f50, 0x0ff00ff0, "usax%c\t%12-15R, %16-19R, %0-3R"},
   {ARM_EXT_V6, 0x06bf0f30, 0x0fff0ff0, "rev%c\t%12-15R, %0-3R"},
   {ARM_EXT_V6, 0x06bf0fb0, 0x0fff0ff0, "rev16%c\t%12-15R, %0-3R"},
   {ARM_EXT_V6, 0x06ff0fb0, 0x0fff0ff0, "revsh%c\t%12-15R, %0-3R"},
@@ -1151,6 +1162,7 @@ static const struct opcode32 arm_opcodes[] =
    %x                  print warning if conditional an not at end of IT block"
    %X                  print "\t; unpredictable <IT:code>" if conditional
    %I                  print IT instruction suffix and operands
+   %W                  print Thumb Writeback indicator for LDMIA
    %<bitfield>r                print bitfield as an ARM register
    %<bitfield>d                print bitfield as a decimal
    %<bitfield>H         print (bitfield * 2) as a decimal
@@ -1238,6 +1250,7 @@ static const struct opcode16 thumb_opcodes[] =
   {ARM_EXT_V4T, 0x5000, 0xFA00, "str%10'b%c\t%0-2r, [%3-5r, %6-8r]"},
   {ARM_EXT_V4T, 0x5800, 0xFA00, "ldr%10'b%c\t%0-2r, [%3-5r, %6-8r]"},
   /* format 1 */
+  {ARM_EXT_V4T, 0x0000, 0xFFC0, "mov%C\t%0-2r, %3-5r"},
   {ARM_EXT_V4T, 0x0000, 0xF800, "lsl%C\t%0-2r, %3-5r, #%6-10d"},
   {ARM_EXT_V4T, 0x0800, 0xF800, "lsr%C\t%0-2r, %3-5r, %s"},
   {ARM_EXT_V4T, 0x1000, 0xF800, "asr%C\t%0-2r, %3-5r, %s"},
@@ -1264,7 +1277,7 @@ static const struct opcode16 thumb_opcodes[] =
   {ARM_EXT_V4T, 0xA800, 0xF800, "add%c\t%8-10r, sp, #%0-7W"},
   /* format 15 */
   {ARM_EXT_V4T, 0xC000, 0xF800, "stmia%c\t%8-10r!, %M"},
-  {ARM_EXT_V4T, 0xC800, 0xF800, "ldmia%c\t%8-10r!, %M"},
+  {ARM_EXT_V4T, 0xC800, 0xF800, "ldmia%c\t%8-10r%W, %M"},
   /* format 17 */
   {ARM_EXT_V4T, 0xDF00, 0xFF00, "svc%c\t%0-7d"},
   /* format 16 */
@@ -1394,12 +1407,12 @@ static const struct opcode32 thumb32_opcodes[] =
   {ARM_EXT_V6T2, 0xfa90f090, 0xfff0f0f0, "rev16%c.w\t%8-11r, %16-19r"},
   {ARM_EXT_V6T2, 0xfa90f0a0, 0xfff0f0f0, "rbit%c\t%8-11r, %16-19r"},
   {ARM_EXT_V6T2, 0xfa90f0b0, 0xfff0f0f0, "revsh%c.w\t%8-11r, %16-19r"},
-  {ARM_EXT_V6T2, 0xfaa0f000, 0xfff0f0f0, "saddsubx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfaa0f010, 0xfff0f0f0, "qaddsubx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfaa0f020, 0xfff0f0f0, "shaddsubx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfaa0f040, 0xfff0f0f0, "uaddsubx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfaa0f050, 0xfff0f0f0, "uqaddsubx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfaa0f060, 0xfff0f0f0, "uhaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f000, 0xfff0f0f0, "sasx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f010, 0xfff0f0f0, "qasx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f020, 0xfff0f0f0, "shasx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f040, 0xfff0f0f0, "uasx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f050, 0xfff0f0f0, "uqasx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f060, 0xfff0f0f0, "uhasx%c\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfaa0f080, 0xfff0f0f0, "sel%c\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfab0f080, 0xfff0f0f0, "clz%c\t%8-11r, %16-19r"},
   {ARM_EXT_V6T2, 0xfac0f000, 0xfff0f0f0, "ssub8%c\t%8-11r, %16-19r, %0-3r"},
@@ -1414,12 +1427,12 @@ static const struct opcode32 thumb32_opcodes[] =
   {ARM_EXT_V6T2, 0xfad0f040, 0xfff0f0f0, "usub16%c\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfad0f050, 0xfff0f0f0, "uqsub16%c\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfad0f060, 0xfff0f0f0, "uhsub16%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfae0f000, 0xfff0f0f0, "ssubaddx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfae0f010, 0xfff0f0f0, "qsubaddx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfae0f020, 0xfff0f0f0, "shsubaddx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfae0f040, 0xfff0f0f0, "usubaddx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfae0f050, 0xfff0f0f0, "uqsubaddx%c\t%8-11r, %16-19r, %0-3r"},
-  {ARM_EXT_V6T2, 0xfae0f060, 0xfff0f0f0, "uhsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f000, 0xfff0f0f0, "ssax%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f010, 0xfff0f0f0, "qsax%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f020, 0xfff0f0f0, "shsax%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f040, 0xfff0f0f0, "usax%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f050, 0xfff0f0f0, "uqsax%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f060, 0xfff0f0f0, "uhsax%c\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfb00f000, 0xfff0f0f0, "mul%c.w\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfb70f000, 0xfff0f0f0, "usad8%c\t%8-11r, %16-19r, %0-3r"},
   {ARM_EXT_V6T2, 0xfa00f000, 0xffe0f0f0, "lsl%20's%c.w\t%8-11R, %16-19R, %0-3R"},
@@ -1750,7 +1763,8 @@ print_insn_coprocessor (bfd_vma pc,
   fprintf_ftype func = info->fprintf_func;
   unsigned long mask;
   unsigned long value = 0;
-  unsigned long allowed_arches = ((arm_feature_set *) info->private_data)->coproc;
+  struct arm_private_data *private_data = info->private_data;
+  unsigned long allowed_arches = private_data->features.coproc;
   int cond;
 
   for (insn = coprocessor_opcodes; insn->assembler; insn++)
@@ -1776,7 +1790,7 @@ print_insn_coprocessor (bfd_vma pc,
            continue;
 
          case SENTINEL_GENERIC_START:
-           allowed_arches = ((arm_feature_set *) info->private_data)->core;
+           allowed_arches = private_data->features.core;
            continue;
 
          default:
@@ -2294,11 +2308,11 @@ print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
 
       func (stream, "[pc");
 
+      if (NEGATIVE_BIT_SET)
+       offset = - offset;
+
       if (PRE_BIT_SET)
        {
-         if (NEGATIVE_BIT_SET)
-           offset = - offset;
-
          /* Pre-indexed.  */
          func (stream, ", #%d]", offset);
 
@@ -2457,7 +2471,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
                          func (stream, "d%d-d%d", rd, rd + n - 1);
                        func (stream, "}, [%s", arm_regnames[rn]);
                        if (align)
-                         func (stream, ", :%d", 32 << align);
+                         func (stream, " :%d", 32 << align);
                        func (stream, "]");
                        if (rm == 0xd)
                          func (stream, "!");
@@ -2532,7 +2546,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
                             rd + i * stride, idx);
                         func (stream, "}, [%s", arm_regnames[rn]);
                        if (align)
-                         func (stream, ", :%d", align);
+                         func (stream, " :%d", align);
                        func (stream, "]");
                        if (rm == 0xd)
                          func (stream, "!");
@@ -2573,9 +2587,9 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
                             if (type == 3)
                               align = (size > 1) ? align >> 1 : align;
                            if (type == 2 || (type == 0 && !size))
-                             func (stream, ", :<bad align %d>", align);
+                             func (stream, " :<bad align %d>", align);
                            else
-                             func (stream, ", :%d", align);
+                             func (stream, " :%d", align);
                          }
                        func (stream, "]");
                        if (rm == 0xd)
@@ -2708,7 +2722,8 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
                               }
                             else
                               func (stream, "#%ld\t; 0x%.8lx",
-                                   (long) (NEGATIVE_BIT_SET ? value | ~0xffffffffL : value),
+                                   (long) (((value & 0x80000000L) != 0) 
+                                           ? value | ~0xffffffffL : value),
                                    value);
                             break;
 
@@ -2843,6 +2858,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info, long given)
   const struct opcode32 *insn;
   void *stream = info->stream;
   fprintf_ftype func = info->fprintf_func;
+  struct arm_private_data *private_data = info->private_data;
 
   if (print_insn_coprocessor (pc, info, given, FALSE))
     return;
@@ -2855,7 +2871,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info, long given)
       if ((given & insn->mask) != insn->value)
        continue;
     
-      if ((insn->arch & ((arm_feature_set *) info->private_data)->core) == 0)
+      if ((insn->arch & private_data->features.core) == 0)
        continue;
 
       /* Special case: an instruction with all bits set in the condition field
@@ -3057,7 +3073,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info, long given)
                          /* The p-variants of tst/cmp/cmn/teq are the pre-V6
                             mechanism for setting PSR flag bits.  They are
                             obsolete in V6 onwards.  */
-                         if (((((arm_feature_set *) info->private_data)->core) & ARM_EXT_V6) == 0)
+                         if ((private_data->features.core & ARM_EXT_V6) == 0)
                            func (stream, "p");
                        }
                      break;
@@ -3140,15 +3156,32 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info, long given)
                      break;
 
                    case 'U':
-                     switch (given & 0xf)
+                     if ((given & 0xf0) == 0x60) 
                        {
-                       case 0xf: func (stream, "sy"); break;
-                       case 0x7: func (stream, "un"); break;
-                       case 0xe: func (stream, "st"); break;
-                       case 0x6: func (stream, "unst"); break;
-                       default:
-                         func (stream, "#%d", (int) given & 0xf);
-                         break;
+                         switch (given & 0xf)
+                           {
+                           case 0xf: func (stream, "sy"); break;
+                           default:
+                             func (stream, "#%d", (int) given & 0xf);
+                             break;
+                           }
+                       } 
+                     else 
+                       {
+                         switch (given & 0xf)
+                           {
+                           case 0xf: func (stream, "sy"); break;
+                           case 0x7: func (stream, "un"); break;
+                           case 0xe: func (stream, "st"); break;
+                           case 0x6: func (stream, "unst"); break;
+                           case 0xb: func (stream, "ish"); break;
+                           case 0xa: func (stream, "ishst"); break;
+                           case 0x3: func (stream, "osh"); break;
+                           case 0x2: func (stream, "oshst"); break;
+                           default:
+                             func (stream, "#%d", (int) given & 0xf);
+                             break;
+                           }
                        }
                      break;
 
@@ -3426,6 +3459,14 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given)
                }
                break;
 
+             case 'W':
+               /* Print writeback indicator for a LDMIA.  We are doing a
+                  writeback if the base register is not in the register
+                  mask.  */
+               if ((given & (1 << ((given & 0x0700) >> 8))) == 0)
+                 func (stream, "!");
+               break;
+
              case 'b':
                /* Print ARM V6T2 CZB address: pc+4+6 bits.  */
                {
@@ -3975,16 +4016,33 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
                break;
 
              case 'U':
-               switch (given & 0xf)
+               if ((given & 0xf0) == 0x60) 
                  {
-                 case 0xf: func (stream, "sy"); break;
-                 case 0x7: func (stream, "un"); break;
-                 case 0xe: func (stream, "st"); break;
-                 case 0x6: func (stream, "unst"); break;
-                 default:
-                   func (stream, "#%d", (int) given & 0xf);
-                   break;
+                   switch (given & 0xf)
+                     {
+                       case 0xf: func (stream, "sy"); break;
+                       default:
+                         func (stream, "#%d", (int) given & 0xf);
+                             break;
+                     }
                  }
+               else 
+                 {
+                   switch (given & 0xf)
+                     {
+                       case 0xf: func (stream, "sy"); break;
+                       case 0x7: func (stream, "un"); break;
+                       case 0xe: func (stream, "st"); break;
+                       case 0x6: func (stream, "unst"); break;
+                       case 0xb: func (stream, "ish"); break;
+                       case 0xa: func (stream, "ishst"); break;
+                       case 0x3: func (stream, "osh"); break;
+                       case 0x2: func (stream, "oshst"); break;
+                       default:
+                         func (stream, "#%d", (int) given & 0xf);
+                         break;
+                     }
+                  }
                break;
 
              case 'C':
@@ -4268,7 +4326,44 @@ find_ifthen_state (bfd_vma pc,
     ifthen_state = 0;
 }
 
-/* Try to infer the code type (Arm or Thumb) from a symbol.
+/* Returns nonzero and sets *MAP_TYPE if the N'th symbol is a
+   mapping symbol.  */
+
+static int
+is_mapping_symbol (struct disassemble_info *info, int n,
+                  enum map_type *map_type)
+{
+  const char *name;
+
+  name = bfd_asymbol_name (info->symtab[n]);
+  if (name[0] == '$' && (name[1] == 'a' || name[1] == 't' || name[1] == 'd')
+      && (name[2] == 0 || name[2] == '.'))
+    {
+      *map_type = ((name[1] == 'a') ? MAP_ARM
+                  : (name[1] == 't') ? MAP_THUMB
+                  : MAP_DATA);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* Try to infer the code type (ARM or Thumb) from a mapping symbol.
+   Returns nonzero if *MAP_TYPE was set.  */
+
+static int
+get_map_sym_type (struct disassemble_info *info,
+                 int n,
+                 enum map_type *map_type)
+{
+  /* If the symbol is in a different section, ignore it.  */
+  if (info->section != NULL && info->section != info->symtab[n]->section)
+    return FALSE;
+
+  return is_mapping_symbol (info, n, map_type);
+}
+
+/* Try to infer the code type (ARM or Thumb) from a non-mapping symbol.
    Returns nonzero if *MAP_TYPE was set.  */
 
 static int
@@ -4278,7 +4373,10 @@ get_sym_code_type (struct disassemble_info *info,
 {
   elf_symbol_type *es;
   unsigned int type;
-  const char *name;
+
+  /* If the symbol is in a different section, ignore it.  */
+  if (info->section != NULL && info->section != info->symtab[n]->section)
+    return FALSE;
 
   es = *(elf_symbol_type **)(info->symtab + n);
   type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
@@ -4290,17 +4388,6 @@ get_sym_code_type (struct disassemble_info *info,
       return TRUE;
     }
 
-  /* Check for mapping symbols.  */
-  name = bfd_asymbol_name (info->symtab[n]);
-  if (name[0] == '$' && (name[1] == 'a' || name[1] == 't' || name[1] == 'd')
-      && (name[2] == 0 || name[2] == '.'))
-    {
-      *map_type = ((name[1] == 'a') ? MAP_ARM
-                  : (name[1] == 't') ? MAP_THUMB
-                  : MAP_DATA);
-      return TRUE;
-    }
-
   return FALSE;
 }
 
@@ -4360,6 +4447,7 @@ print_insn (bfd_vma pc, struct disassemble_info *info, bfd_boolean little)
   unsigned int size = 4;
   void         (*printer) (bfd_vma, struct disassemble_info *, long);
   bfd_boolean   found = FALSE;
+  struct arm_private_data *private_data;
 
   if (info->disassembler_options)
     {
@@ -4372,7 +4460,7 @@ print_insn (bfd_vma pc, struct disassemble_info *info, bfd_boolean little)
   /* PR 10288: Control which instructions will be disassembled.  */
   if (info->private_data == NULL)
     {
-      static arm_feature_set features;
+      static struct arm_private_data private;
 
       if ((info->flags & USER_SPECIFIED_MACHINE_TYPE) == 0)
        /* If the user did not use the -m command line switch then default to
@@ -4398,67 +4486,124 @@ print_insn (bfd_vma pc, struct disassemble_info *info, bfd_boolean little)
       /* Compute the architecture bitmask from the machine number.
         Note: This assumes that the machine number will not change
         during disassembly....  */
-      select_arm_features (info->mach, & features);
+      select_arm_features (info->mach, & private.features);
 
-      info->private_data = & features;
+      private.has_mapping_symbols = -1;
+
+      info->private_data = & private;
     }
-  
+
+  private_data = info->private_data;
+
   /* Decide if our code is going to be little-endian, despite what the
      function argument might say.  */
   little_code = ((info->endian_code == BFD_ENDIAN_LITTLE) || little);
 
-  /* First check the full symtab for a mapping symbol, even if there
-     are no usable non-mapping symbols for this address.  */
+  /* For ELF, consult the symbol table to determine what kind of code
+     or data we have.  */
   if (info->symtab_size != 0
       && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour)
     {
       bfd_vma addr;
-      int n;
+      int n, start;
       int last_sym = -1;
       enum map_type type = MAP_ARM;
 
-      if (pc <= last_mapping_addr)
-       last_mapping_sym = -1;
-      is_thumb = (last_type == MAP_THUMB);
-      found = FALSE;
       /* Start scanning at the start of the function, or wherever
         we finished last time.  */
-      n = info->symtab_pos + 1;
-      if (n < last_mapping_sym)
-       n = last_mapping_sym;
+      start = info->symtab_pos + 1;
+      if (start < last_mapping_sym)
+       start = last_mapping_sym;
+      found = FALSE;
 
-      /* Scan up to the location being disassembled.  */
-      for (; n < info->symtab_size; n++)
+      /* First, look for mapping symbols.  */
+      if (private_data->has_mapping_symbols != 0)
        {
-         addr = bfd_asymbol_value (info->symtab[n]);
-         if (addr > pc)
-           break;
-         if ((info->section == NULL
-              || info->section == info->symtab[n]->section)
-             && get_sym_code_type (info, n, &type))
+         /* Scan up to the location being disassembled.  */
+         for (n = start; n < info->symtab_size; n++)
            {
-             last_sym = n;
+             addr = bfd_asymbol_value (info->symtab[n]);
+             if (addr > pc)
+               break;
+             if (get_map_sym_type (info, n, &type))
+               {
+                 last_sym = n;
+                 found = TRUE;
+               }
+           }
+
+         if (!found)
+           {
+             /* No mapping symbol found at this address.  Look backwards
+                for a preceeding one.  */
+             for (n = start - 1; n >= 0; n--)
+               {
+                 if (get_map_sym_type (info, n, &type))
+                   {
+                     last_sym = n;
+                     found = TRUE;
+                     break;
+                   }
+               }
+           }
+
+         if (found)
+           private_data->has_mapping_symbols = 1;
+
+         /* No mapping symbols were found.  A leading $d may be
+            omitted for sections which start with data; but for
+            compatibility with legacy and stripped binaries, only
+            assume the leading $d if there is at least one mapping
+            symbol in the file.  */
+         if (!found && private_data->has_mapping_symbols == -1)
+           {
+             /* Look for mapping symbols, in any section.  */
+             for (n = 0; n < info->symtab_size; n++)
+               if (is_mapping_symbol (info, n, &type))
+                 {
+                   private_data->has_mapping_symbols = 1;
+                   break;
+                 }
+             if (private_data->has_mapping_symbols == -1)
+               private_data->has_mapping_symbols = 0;
+           }
+
+         if (!found && private_data->has_mapping_symbols == 1)
+           {
+             type = MAP_DATA;
              found = TRUE;
            }
        }
 
+      /* Next search for function symbols to separate ARM from Thumb
+        in binaries without mapping symbols.  */
       if (!found)
        {
-         n = info->symtab_pos;
-         if (n < last_mapping_sym - 1)
-           n = last_mapping_sym - 1;
-
-         /* No mapping symbol found at this address.  Look backwards
-            for a preceeding one.  */
-         for (; n >= 0; n--)
+         /* Scan up to the location being disassembled.  */
+         for (n = start; n < info->symtab_size; n++)
            {
-             if ((info->section == NULL
-                  || info->section == info->symtab[n]->section)
-                 && get_sym_code_type (info, n, &type))
+             addr = bfd_asymbol_value (info->symtab[n]);
+             if (addr > pc)
+               break;
+             if (get_sym_code_type (info, n, &type))
                {
                  last_sym = n;
                  found = TRUE;
-                 break;
+               }
+           }
+
+         if (!found)
+           {
+             /* No mapping symbol found at this address.  Look backwards
+                for a preceeding one.  */
+             for (n = start - 1; n >= 0; n--)
+               {
+                 if (get_sym_code_type (info, n, &type))
+                   {
+                     last_sym = n;
+                     found = TRUE;
+                     break;
+                   }
                }
            }
        }
@@ -4478,7 +4623,9 @@ print_insn (bfd_vma pc, struct disassemble_info *info, bfd_boolean little)
          for (n = last_sym + 1; n < info->symtab_size; n++)
            {
              addr = bfd_asymbol_value (info->symtab[n]);
-             if (addr > pc)
+             if (addr > pc
+                 && (info->section == NULL
+                     || info->section == info->symtab[n]->section))
                {
                  if (addr - pc < size)
                    size = addr - pc;