qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/5] Fix Thumb-1 BE32 execution and disassembly.


From: Julian Brown
Subject: [Qemu-devel] [PATCH 2/5] Fix Thumb-1 BE32 execution and disassembly.
Date: Thu, 3 Nov 2016 10:30:55 -0700

Thumb-1 code has some issues in BE32 mode (as currently implemented). In
short, since bytes are swapped within words at load time for BE32
executables, this also swaps pairs of adjacent Thumb-1 instructions.

This patch un-swaps those pairs of instructions again, both for execution,
and for disassembly.

Signed-off-by: Julian Brown <address@hidden>
---
 disas/arm.c           | 46 +++++++++++++++++++++++++++++++++++-----------
 include/disas/bfd.h   |  1 +
 target-arm/arm_ldst.h | 10 +++++++++-
 target-arm/cpu.c      |  4 ++++
 4 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/disas/arm.c b/disas/arm.c
index 93c6503..4807ba3 100644
--- a/disas/arm.c
+++ b/disas/arm.c
@@ -3863,10 +3863,11 @@ print_insn_arm (bfd_vma pc, struct disassemble_info 
*info)
   int           is_data = false;
   unsigned int size = 4;
   void         (*printer) (bfd_vma, struct disassemble_info *, long);
-  int little;
+  int little, is_thumb1_be32 = false;
 
   little = (info->endian == BFD_ENDIAN_LITTLE);
   is_thumb |= (pc & 1);
+  is_thumb1_be32 = (info->flags & INSN_ARM_THUMB1_BE32) != 0;
   pc &= ~(bfd_vma)1;
 
   if (force_thumb)
@@ -3915,11 +3916,22 @@ print_insn_arm (bfd_vma pc, struct disassemble_info 
*info)
       info->bytes_per_chunk = 2;
       size = 2;
 
-      status = info->read_memory_func (pc, (bfd_byte *)b, 2, info);
-      if (little)
-       given = (b[0]) | (b[1] << 8);
-      else
-       given = (b[1]) | (b[0] << 8);
+      if (is_thumb1_be32) {
+          status = info->read_memory_func(pc & ~3, (bfd_byte *)b, 4, info);
+          assert(little);
+          if ((pc & 2) == 0) {
+              given = b[2] | (b[3] << 8);
+          } else {
+              given = b[0] | (b[1] << 8);
+          }
+      } else {
+          status = info->read_memory_func(pc, (bfd_byte *)b, 2, info);
+          if (little) {
+              given = (b[0]) | (b[1] << 8);
+          } else {
+              given = (b[1]) | (b[0] << 8);
+          }
+      }
 
       if (!status)
        {
@@ -3929,11 +3941,23 @@ print_insn_arm (bfd_vma pc, struct disassemble_info 
*info)
              || (given & 0xF800) == 0xF000
              || (given & 0xF800) == 0xE800)
            {
-             status = info->read_memory_func (pc + 2, (bfd_byte *)b, 2, info);
-             if (little)
-               given = (b[0]) | (b[1] << 8) | (given << 16);
-             else
-               given = (b[1]) | (b[0] << 8) | (given << 16);
+              if (is_thumb1_be32) {
+                  status = info->read_memory_func((pc + 2) & ~3,
+                                                  (bfd_byte *)b, 4, info);
+                  if (((pc + 2) & 2) == 0) {
+                      given = b[2] | (b[3] << 8) | (given << 16);
+                  } else {
+                      given = b[0] | (b[1] << 8) | (given << 16);
+                  }
+              } else {
+                  status = info->read_memory_func(pc + 2, (bfd_byte *)b, 2,
+                                                  info);
+                  if (little) {
+                      given = (b[0]) | (b[1] << 8) | (given << 16);
+                  } else {
+                      given = (b[1]) | (b[0] << 8) | (given << 16);
+                  }
+              }
 
              printer = print_insn_thumb32;
              size = 4;
diff --git a/include/disas/bfd.h b/include/disas/bfd.h
index 8a3488c..76ff6a0 100644
--- a/include/disas/bfd.h
+++ b/include/disas/bfd.h
@@ -291,6 +291,7 @@ typedef struct disassemble_info {
      The bottom 16 bits are for the internal use of the disassembler.  */
   unsigned long flags;
 #define INSN_HAS_RELOC 0x80000000
+#define INSN_ARM_THUMB1_BE32 0x00010000
   PTR private_data;
 
   /* Function used to get bytes to disassemble.  MEMADDR is the
diff --git a/target-arm/arm_ldst.h b/target-arm/arm_ldst.h
index a76d89f..01587b3 100644
--- a/target-arm/arm_ldst.h
+++ b/target-arm/arm_ldst.h
@@ -39,7 +39,15 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, 
target_ulong addr,
 static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
                                      bool sctlr_b)
 {
-    uint16_t insn = cpu_lduw_code(env, addr);
+    uint16_t insn;
+#ifndef CONFIG_USER_ONLY
+    /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped
+       within each word.  Undo that now.  */
+    if (sctlr_b) {
+        addr ^= 2;
+    }
+#endif
+    insn = cpu_lduw_code(env, addr);
     if (bswap_code(sctlr_b)) {
         return bswap16(insn);
     }
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 2918b24..b9d7393 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -423,6 +423,10 @@ static void arm_disas_set_info(CPUState *cpu, 
disassemble_info *info)
 #endif
     } else if (env->thumb) {
         info->print_insn = print_insn_thumb1;
+        info->flags &= ~INSN_ARM_THUMB1_BE32;
+        if (arm_sctlr_b(env)) {
+            info->flags |= INSN_ARM_THUMB1_BE32;
+        }
     } else {
         info->print_insn = print_insn_arm;
     }
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]