[v2,04/17] arm-tdep.c: Use relocation visitor in ARM instruction decoding

Message ID 1465476975-25062-5-git-send-email-antoine.tremblay@ericsson.com
State New, archived
Headers

Commit Message

Antoine Tremblay June 9, 2016, 12:56 p.m. UTC
  From: Simon Marchi <simon.marchi@ericsson.com>

In order to be able to use the instruction decoding code for other
purposes than displaced stepping, we have to decouple the decoding phase
from the relocation, which is specific to the task of displaced
stepping.  In other words, the arm_decode_* functions should not call the
arm_copy_* functions explicitly.

This is achieved by passing a structure of callback functions (the
visitor) to the decoding functions.  Each of the callback corresponds to
a type of instruction that probably needs to be modified when it is
executed at another location.  The user/caller of the decoding "subsystem"
is responsible for providing this structure of callbacks.

I renamed arm_process_displaced_insn_arm to arm_relocate_insn_arm, since
it's not longer specific do displaced stepping.

This patch shouldn't introduce functional changes, as it is only a
refactoring.

gdb/ChangeLog:

	* arm-tdep.c (struct arm_insn_reloc_visitor): New structure.
	(arm_decode_misc_memhint_neon): Add visitor parameter and use
	it.
	(arm_decode_unconditional): Likewise.
	(arm_decode_miscellaneous): Likewise.
	(arm_decode_dp_misc): Likewise.
	(arm_decode_ld_st_word_ubyte): Likewise.
	(arm_decode_media): Likewise.
	(arm_decode_b_bl_ldmstm): Likewise.
	(arm_decode_ext_reg_ld_st): Likewise.
	(arm_decode_svc_copro): Likewise.
	(arm_process_displaced_insn_arm): Rename to...
	(arm_relocate_insn_arm): ... this, add visitor parameter and use
	it.
	(arm_insn_reloc_visitor): New variable.
	(arm_process_displaced_insn): Pass pointer to
	arm_insn_reloc_visitor to arm_relocate_insn_arm.
---
 gdb/arm-tdep.c | 308 +++++++++++++++++++++++++++++++++------------------------
 1 file changed, 181 insertions(+), 127 deletions(-)
  

Patch

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 0c05aae..dcd65eeb 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -4480,6 +4480,28 @@  struct arm_insn_reloc_data
   struct regcache *regs;
 };
 
+struct arm_insn_reloc_visitor
+{
+  int (*alu_imm) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*alu_reg) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*alu_shifted_reg) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*b_bl_blx) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*block_xfer) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*bx_blx_reg) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*copro_load_store) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*extra_ld_st) (uint32_t insn, struct arm_insn_reloc_data *data,
+		      int unprivileged);
+  int (*ldr_str_ldrb_strb) (uint32_t insn, struct arm_insn_reloc_data *data,
+			    int load, int size, int usermode);
+  int (*others) (uint32_t insn, const char *iname,
+		 struct arm_insn_reloc_data *data);
+  int (*preload) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*preload_reg) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*svc) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*undef) (uint32_t insn, struct arm_insn_reloc_data *data);
+  int (*unpred) (uint32_t insn, struct arm_insn_reloc_data *data);
+};
+
 /* Helper for register reads for displaced stepping.  In particular, this
    returns the PC as it would be seen by the instruction at its original
    location.  */
@@ -6462,91 +6484,94 @@  arm_copy_unpred (uint32_t insn, struct arm_insn_reloc_data *data)
    the presentation in the ARM ARM.  */
 
 static int
-arm_decode_misc_memhint_neon (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_misc_memhint_neon (uint32_t insn,
+			      struct arm_insn_reloc_visitor *visitor,
+			      struct arm_insn_reloc_data *data)
 {
   unsigned int op1 = bits (insn, 20, 26), op2 = bits (insn, 4, 7);
   unsigned int rn = bits (insn, 16, 19);
 
   if (op1 == 0x10 && (op2 & 0x2) == 0x0 && (rn & 0xe) == 0x0)
-    return arm_copy_unmodified (insn, "cps", data);
+    return visitor->others (insn, "cps", data);
   else if (op1 == 0x10 && op2 == 0x0 && (rn & 0xe) == 0x1)
-    return arm_copy_unmodified (insn, "setend", data);
+    return visitor->others (insn, "setend", data);
   else if ((op1 & 0x60) == 0x20)
-    return arm_copy_unmodified (insn, "neon dataproc", data);
+    return visitor->others (insn, "neon dataproc", data);
   else if ((op1 & 0x71) == 0x40)
-    return arm_copy_unmodified (insn, "neon elt/struct load/store", data);
+    return visitor->others (insn, "neon elt/struct load/store", data);
   else if ((op1 & 0x77) == 0x41)
-    return arm_copy_unmodified (insn, "unallocated mem hint", data);
+    return visitor->others (insn, "unallocated mem hint", data);
   else if ((op1 & 0x77) == 0x45)
-    return arm_copy_preload (insn, data);  /* pli.  */
+    return visitor->preload (insn, data);  /* pli.  */
   else if ((op1 & 0x77) == 0x51)
     {
       if (rn != 0xf)
-	return arm_copy_preload (insn, data);  /* pld/pldw.  */
+	return visitor->preload (insn, data);  /* pld/pldw.  */
       else
-	return arm_copy_unpred (insn, data);
+	return visitor->unpred (insn, data);
     }
   else if ((op1 & 0x77) == 0x55)
-    return arm_copy_preload (insn, data);  /* pld/pldw.  */
+    return visitor->preload (insn, data);  /* pld/pldw.  */
   else if (op1 == 0x57)
     switch (op2)
       {
-      case 0x1: return arm_copy_unmodified (insn, "clrex", data);
-      case 0x4: return arm_copy_unmodified (insn, "dsb", data);
-      case 0x5: return arm_copy_unmodified (insn, "dmb", data);
-      case 0x6: return arm_copy_unmodified (insn, "isb", data);
-      default: return arm_copy_unpred (insn, data);
+      case 0x1: return visitor->others (insn, "clrex", data);
+      case 0x4: return visitor->others (insn, "dsb", data);
+      case 0x5: return visitor->others (insn, "dmb", data);
+      case 0x6: return visitor->others (insn, "isb", data);
+      default: return visitor->unpred (insn, data);
       }
   else if ((op1 & 0x63) == 0x43)
-    return arm_copy_unpred (insn, data);
+    return visitor->unpred (insn, data);
   else if ((op2 & 0x1) == 0x0)
     switch (op1 & ~0x80)
       {
       case 0x61:
-	return arm_copy_unmodified (insn, "unallocated mem hint", data);
+	return visitor->others (insn, "unallocated mem hint", data);
       case 0x65:
-	return arm_copy_preload_reg (insn, data);  /* pli reg.  */
+	return visitor->preload_reg (insn, data);  /* pli reg.  */
       case 0x71: case 0x75:
         /* pld/pldw reg.  */
-	return arm_copy_preload_reg (insn, data);
+	return visitor->preload_reg (insn, data);
       case 0x63: case 0x67: case 0x73: case 0x77:
-	return arm_copy_unpred (insn, data);
+	return visitor->unpred (insn, data);
       default:
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
       }
   else
-    return arm_copy_undef (insn, data);  /* Probably unreachable.  */
+    return visitor->undef (insn, data);  /* Probably unreachable.  */
 }
 
 static int
-arm_decode_unconditional (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_unconditional (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+			  struct arm_insn_reloc_data *data)
 {
   if (bit (insn, 27) == 0)
-    return arm_decode_misc_memhint_neon (insn, data);
+    return arm_decode_misc_memhint_neon (insn, visitor, data);
   /* Switch on bits: 0bxxxxx321xxx0xxxxxxxxxxxxxxxxxxxx.  */
   else switch (((insn & 0x7000000) >> 23) | ((insn & 0x100000) >> 20))
     {
     case 0x0: case 0x2:
-      return arm_copy_unmodified (insn, "srs", data);
+      return visitor->others (insn, "srs", data);
 
     case 0x1: case 0x3:
-      return arm_copy_unmodified (insn, "rfe", data);
+      return visitor->others (insn, "rfe", data);
 
     case 0x4: case 0x5: case 0x6: case 0x7:
-      return arm_copy_b_bl_blx (insn, data);
+      return visitor->b_bl_blx (insn, data);
 
     case 0x8:
       switch ((insn & 0xe00000) >> 21)
 	{
 	case 0x1: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
 	  /* stc/stc2.  */
-	  return arm_copy_copro_load_store (insn, data);
+	  return visitor->copro_load_store (insn, data);
 
 	case 0x2:
-	  return arm_copy_unmodified (insn, "mcrr/mcrr2", data);
+	  return visitor->others (insn, "mcrr/mcrr2", data);
 
 	default:
-	  return arm_copy_undef (insn, data);
+	  return visitor->undef (insn, data);
 	}
 
     case 0x9:
@@ -6556,53 +6581,54 @@  arm_decode_unconditional (uint32_t insn, struct arm_insn_reloc_data *data)
 	  {
 	  case 0x1: case 0x3:
 	    /* ldc/ldc2 imm (undefined for rn == pc).  */
-	    return rn_f ? arm_copy_undef (insn, data)
-			: arm_copy_copro_load_store (insn, data);
+	    return rn_f ? visitor->undef (insn, data)
+			: visitor->copro_load_store (insn, data);
 
 	  case 0x2:
-	    return arm_copy_unmodified (insn, "mrrc/mrrc2", data);
+	    return visitor->others (insn, "mrrc/mrrc2", data);
 
 	  case 0x4: case 0x5: case 0x6: case 0x7:
 	    /* ldc/ldc2 lit (undefined for rn != pc).  */
-	    return rn_f ? arm_copy_copro_load_store (insn, data)
-			: arm_copy_undef (insn, data);
+	    return rn_f ? visitor->copro_load_store (insn, data)
+			: visitor->undef (insn, data);
 
 	  default:
-	    return arm_copy_undef (insn, data);
+	    return visitor->undef (insn, data);
 	  }
       }
 
     case 0xa:
-      return arm_copy_unmodified (insn, "stc/stc2", data);
+      return visitor->others (insn, "stc/stc2", data);
 
     case 0xb:
       if (bits (insn, 16, 19) == 0xf)
         /* ldc/ldc2 lit.  */
-	return arm_copy_copro_load_store (insn, data);
+	return visitor->copro_load_store (insn, data);
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
 
     case 0xc:
       if (bit (insn, 4))
-	return arm_copy_unmodified (insn, "mcr/mcr2", data);
+	return visitor->others (insn, "mcr/mcr2", data);
       else
-	return arm_copy_unmodified (insn, "cdp/cdp2", data);
+	return visitor->others (insn, "cdp/cdp2", data);
 
     case 0xd:
       if (bit (insn, 4))
-	return arm_copy_unmodified (insn, "mrc/mrc2", data);
+	return visitor->others (insn, "mrc/mrc2", data);
       else
-	return arm_copy_unmodified (insn, "cdp/cdp2", data);
+	return visitor->others (insn, "cdp/cdp2", data);
 
     default:
-      return arm_copy_undef (insn, data);
+      return visitor->undef (insn, data);
     }
 }
 
 /* Decode miscellaneous instructions in dp/misc encoding space.  */
 
 static int
-arm_decode_miscellaneous (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_miscellaneous (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+			  struct arm_insn_reloc_data *data)
 {
   unsigned int op2 = bits (insn, 4, 6);
   unsigned int op = bits (insn, 21, 22);
@@ -6610,81 +6636,83 @@  arm_decode_miscellaneous (uint32_t insn, struct arm_insn_reloc_data *data)
   switch (op2)
     {
     case 0x0:
-      return arm_copy_unmodified (insn, "mrs/msr", data);
+      return visitor->others (insn, "mrs/msr", data);
 
     case 0x1:
       if (op == 0x1)  /* bx.  */
-	return arm_copy_bx_blx_reg (insn, data);
+	return visitor->bx_blx_reg (insn, data);
       else if (op == 0x3)
-	return arm_copy_unmodified (insn, "clz", data);
+	return visitor->others (insn, "clz", data);
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
 
     case 0x2:
       if (op == 0x1)
-        /* Not really supported.  */
-	return arm_copy_unmodified (insn, "bxj", data);
+	/* Not really supported.  */
+	return visitor->others (insn, "bxj", data);
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
 
     case 0x3:
       if (op == 0x1)
-	return arm_copy_bx_blx_reg (insn, data);  /* blx register.  */
+	return visitor->bx_blx_reg (insn, data);  /* blx register.  */
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
 
     case 0x5:
-      return arm_copy_unmodified (insn, "saturating add/sub", data);
+      return visitor->others (insn, "saturating add/sub", data);
 
     case 0x7:
       if (op == 0x1)
-	return arm_copy_unmodified (insn, "bkpt", data);
+	return visitor->others (insn, "bkpt", data);
       else if (op == 0x3)
-        /* Not really supported.  */
-	return arm_copy_unmodified (insn, "smc", data);
+	/* Not really supported.  */
+	return visitor->others (insn, "smc", data);
 
     default:
-      return arm_copy_undef (insn, data);
+      return visitor->undef (insn, data);
     }
 }
 
 static int
-arm_decode_dp_misc (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_dp_misc (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+		    struct arm_insn_reloc_data *data)
 {
   if (bit (insn, 25))
     switch (bits (insn, 20, 24))
       {
       case 0x10:
-	return arm_copy_unmodified (insn, "movw", data);
+	return visitor->others (insn, "movw", data);
 
       case 0x14:
-	return arm_copy_unmodified (insn, "movt", data);
+	return visitor->others (insn, "movt", data);
 
-      case 0x12: case 0x16:
-	return arm_copy_unmodified (insn, "msr imm", data);
+      case 0x12:
+      case 0x16:
+	return visitor->others (insn, "msr imm", data);
 
       default:
-	return arm_copy_alu_imm (insn, data);
+	return visitor->alu_imm (insn, data);
       }
   else
     {
       uint32_t op1 = bits (insn, 20, 24), op2 = bits (insn, 4, 7);
 
       if ((op1 & 0x19) != 0x10 && (op2 & 0x1) == 0x0)
-	return arm_copy_alu_reg (insn, data);
+	return visitor->alu_reg (insn, data);
       else if ((op1 & 0x19) != 0x10 && (op2 & 0x9) == 0x1)
-	return arm_copy_alu_shifted_reg (insn, data);
+	return visitor->alu_shifted_reg (insn, data);
       else if ((op1 & 0x19) == 0x10 && (op2 & 0x8) == 0x0)
-	return arm_decode_miscellaneous (insn, data);
+	return arm_decode_miscellaneous (insn, visitor, data);
       else if ((op1 & 0x19) == 0x10 && (op2 & 0x9) == 0x8)
-	return arm_copy_unmodified (insn, "halfword mul/mla", data);
+	return visitor->others (insn, "halfword mul/mla", data);
       else if ((op1 & 0x10) == 0x00 && op2 == 0x9)
-	return arm_copy_unmodified (insn, "mul/mla", data);
+	return visitor->others (insn, "mul/mla", data);
       else if ((op1 & 0x10) == 0x10 && op2 == 0x9)
-	return arm_copy_unmodified (insn, "synch", data);
+	return visitor->others (insn, "synch", data);
       else if (op2 == 0xb || (op2 & 0xd) == 0xd)
 	/* 2nd arg means "unprivileged".  */
-	return arm_copy_extra_ld_st (insn, data, (op1 & 0x12) == 0x02);
+	return visitor->extra_ld_st (insn, data, (op1 & 0x12) == 0x02);
     }
 
   /* Should be unreachable.  */
@@ -6692,89 +6720,92 @@  arm_decode_dp_misc (uint32_t insn, struct arm_insn_reloc_data *data)
 }
 
 static int
-arm_decode_ld_st_word_ubyte (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_ld_st_word_ubyte (uint32_t insn,
+			     struct arm_insn_reloc_visitor *visitor,
+			     struct arm_insn_reloc_data *data)
 {
   int a = bit (insn, 25), b = bit (insn, 4);
   uint32_t op1 = bits (insn, 20, 24);
 
   if ((!a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02)
       || (a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 0, 4, 0);
+    return visitor->ldr_str_ldrb_strb (insn, data, 0, 4, 0);
   else if ((!a && (op1 & 0x17) == 0x02)
 	    || (a && (op1 & 0x17) == 0x02 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 0, 4, 1);
+    return visitor->ldr_str_ldrb_strb (insn, data, 0, 4, 1);
   else if ((!a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03)
 	    || (a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 1, 4, 0);
+    return visitor->ldr_str_ldrb_strb (insn, data, 1, 4, 0);
   else if ((!a && (op1 & 0x17) == 0x03)
 	   || (a && (op1 & 0x17) == 0x03 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 1, 4, 1);
+    return visitor->ldr_str_ldrb_strb (insn, data, 1, 4, 1);
   else if ((!a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06)
 	    || (a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 0, 1, 0);
+    return visitor->ldr_str_ldrb_strb (insn, data, 0, 1, 0);
   else if ((!a && (op1 & 0x17) == 0x06)
 	   || (a && (op1 & 0x17) == 0x06 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 0, 1, 1);
+    return visitor->ldr_str_ldrb_strb (insn, data, 0, 1, 1);
   else if ((!a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07)
 	   || (a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 1, 1, 0);
+    return visitor->ldr_str_ldrb_strb (insn, data, 1, 1, 0);
   else if ((!a && (op1 & 0x17) == 0x07)
 	   || (a && (op1 & 0x17) == 0x07 && !b))
-    return arm_copy_ldr_str_ldrb_strb (insn, data, 1, 1, 1);
+    return visitor->ldr_str_ldrb_strb (insn, data, 1, 1, 1);
 
   /* Should be unreachable.  */
   return 1;
 }
 
 static int
-arm_decode_media (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_media (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+		  struct arm_insn_reloc_data *data)
 {
   switch (bits (insn, 20, 24))
     {
     case 0x00: case 0x01: case 0x02: case 0x03:
-      return arm_copy_unmodified (insn, "parallel add/sub signed", data);
+      return visitor->others (insn, "parallel add/sub signed", data);
 
     case 0x04: case 0x05: case 0x06: case 0x07:
-      return arm_copy_unmodified (insn, "parallel add/sub unsigned", data);
+      return visitor->others (insn, "parallel add/sub unsigned", data);
 
     case 0x08: case 0x09: case 0x0a: case 0x0b:
     case 0x0c: case 0x0d: case 0x0e: case 0x0f:
-      return arm_copy_unmodified (insn,
-			      "decode/pack/unpack/saturate/reverse", data);
+      return visitor->others (insn, "decode/pack/unpack/saturate/reverse",
+			      data);
 
     case 0x18:
       if (bits (insn, 5, 7) == 0)  /* op2.  */
 	 {
 	  if (bits (insn, 12, 15) == 0xf)
-	    return arm_copy_unmodified (insn, "usad8", data);
+	    return visitor->others (insn, "usad8", data);
 	  else
-	    return arm_copy_unmodified (insn, "usada8", data);
+	    return visitor->others (insn, "usada8", data);
 	}
       else
-	 return arm_copy_undef (insn, data);
+	 return visitor->undef (insn, data);
 
     case 0x1a: case 0x1b:
       if (bits (insn, 5, 6) == 0x2)  /* op2[1:0].  */
-	return arm_copy_unmodified (insn, "sbfx", data);
+	return visitor->others (insn, "sbfx", data);
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
 
     case 0x1c: case 0x1d:
       if (bits (insn, 5, 6) == 0x0)  /* op2[1:0].  */
 	 {
 	  if (bits (insn, 0, 3) == 0xf)
-	    return arm_copy_unmodified (insn, "bfc", data);
+	    return visitor->others (insn, "bfc", data);
 	  else
-	    return arm_copy_unmodified (insn, "bfi", data);
+	    return visitor->others (insn, "bfi", data);
 	}
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
 
     case 0x1e: case 0x1f:
       if (bits (insn, 5, 6) == 0x2)  /* op2[1:0].  */
-	return arm_copy_unmodified (insn, "ubfx", data);
+	return visitor->others (insn, "ubfx", data);
       else
-	return arm_copy_undef (insn, data);
+	return visitor->undef (insn, data);
     }
 
   /* Should be unreachable.  */
@@ -6782,37 +6813,39 @@  arm_decode_media (uint32_t insn, struct arm_insn_reloc_data *data)
 }
 
 static int
-arm_decode_b_bl_ldmstm (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_b_bl_ldmstm (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+			struct arm_insn_reloc_data *data)
 {
   if (bit (insn, 25))
-    return arm_copy_b_bl_blx (insn, data);
+    return visitor->b_bl_blx (insn, data);
   else
-    return arm_copy_block_xfer (insn, data);
+    return visitor->block_xfer (insn, data);
 }
 
 static int
-arm_decode_ext_reg_ld_st (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_ext_reg_ld_st (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+			  struct arm_insn_reloc_data *data)
 {
   unsigned int opcode = bits (insn, 20, 24);
 
   switch (opcode)
     {
     case 0x04: case 0x05:  /* VFP/Neon mrrc/mcrr.  */
-      return arm_copy_unmodified (insn, "vfp/neon mrrc/mcrr", data);
+      return visitor->others (insn, "vfp/neon mrrc/mcrr", data);
 
     case 0x08: case 0x0a: case 0x0c: case 0x0e:
     case 0x12: case 0x16:
-      return arm_copy_unmodified (insn, "vfp/neon vstm/vpush", data);
+      return visitor->others (insn, "vfp/neon vstm/vpush", data);
 
     case 0x09: case 0x0b: case 0x0d: case 0x0f:
     case 0x13: case 0x17:
-      return arm_copy_unmodified (insn, "vfp/neon vldm/vpop", data);
+      return visitor->others (insn, "vfp/neon vldm/vpop", data);
 
     case 0x10: case 0x14: case 0x18: case 0x1c:  /* vstr.  */
     case 0x11: case 0x15: case 0x19: case 0x1d:  /* vldr.  */
       /* Note: no writeback for these instructions.  Bit 25 will always be
 	 zero though (via caller), so the following works OK.  */
-      return arm_copy_copro_load_store (insn, data);
+      return visitor->copro_load_store (insn, data);
     }
 
   /* Should be unreachable.  */
@@ -6874,47 +6907,48 @@  thumb2_decode_ext_reg_ld_st (uint16_t insn1, uint16_t insn2,
 }
 
 static int
-arm_decode_svc_copro (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_decode_svc_copro (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+		      struct arm_insn_reloc_data *data)
 {
   unsigned int op1 = bits (insn, 20, 25);
   int op = bit (insn, 4);
   unsigned int coproc = bits (insn, 8, 11);
 
   if ((op1 & 0x20) == 0x00 && (op1 & 0x3a) != 0x00 && (coproc & 0xe) == 0xa)
-    return arm_decode_ext_reg_ld_st (insn, data);
+    return arm_decode_ext_reg_ld_st (insn, visitor, data);
   else if ((op1 & 0x21) == 0x00 && (op1 & 0x3a) != 0x00
 	   && (coproc & 0xe) != 0xa)
     /* stc/stc2.  */
-    return arm_copy_copro_load_store (insn, data);
+    return visitor->copro_load_store (insn, data);
   else if ((op1 & 0x21) == 0x01 && (op1 & 0x3a) != 0x00
 	   && (coproc & 0xe) != 0xa)
     /* ldc/ldc2 imm/lit.  */
-    return arm_copy_copro_load_store (insn, data);
+    return visitor->copro_load_store (insn, data);
   else if ((op1 & 0x3e) == 0x00)
-    return arm_copy_undef (insn, data);
+    return visitor->undef (insn, data);
   else if ((op1 & 0x3e) == 0x04 && (coproc & 0xe) == 0xa)
-    return arm_copy_unmodified (insn, "neon 64bit xfer", data);
+    return visitor->others (insn, "neon 64bit xfer", data);
   else if (op1 == 0x04 && (coproc & 0xe) != 0xa)
-    return arm_copy_unmodified (insn, "mcrr/mcrr2", data);
+    return visitor->others (insn, "mcrr/mcrr2", data);
   else if (op1 == 0x05 && (coproc & 0xe) != 0xa)
-    return arm_copy_unmodified (insn, "mrrc/mrrc2", data);
+    return visitor->others (insn, "mrrc/mrrc2", data);
   else if ((op1 & 0x30) == 0x20 && !op)
     {
       if ((coproc & 0xe) == 0xa)
-	return arm_copy_unmodified (insn, "vfp dataproc", data);
+	return visitor->others (insn, "vfp dataproc", data);
       else
-	return arm_copy_unmodified (insn, "cdp/cdp2", data);
+	return visitor->others (insn, "cdp/cdp2", data);
     }
   else if ((op1 & 0x30) == 0x20 && op)
-    return arm_copy_unmodified (insn, "neon 8/16/32 bit xfer", data);
+    return visitor->others (insn, "neon 8/16/32 bit xfer", data);
   else if ((op1 & 0x31) == 0x20 && op && (coproc & 0xe) != 0xa)
-    return arm_copy_unmodified (insn, "mcr/mcr2", data);
+    return visitor->others (insn, "mcr/mcr2", data);
   else if ((op1 & 0x31) == 0x21 && op && (coproc & 0xe) != 0xa)
-    return arm_copy_unmodified (insn, "mrc/mrc2", data);
+    return visitor->others (insn, "mrc/mrc2", data);
   else if ((op1 & 0x30) == 0x30)
-    return arm_copy_svc (insn, data);
+    return visitor->svc (insn, data);
   else
-    return arm_copy_undef (insn, data);  /* Possibly unreachable.  */
+    return visitor->undef (insn, data);  /* Possibly unreachable.  */
 }
 
 static int
@@ -7542,38 +7576,58 @@  thumb_32bit_relocate_insn (uint16_t insn1, uint16_t insn2,
 }
 
 static int
-arm_relocate_insn (uint32_t insn, struct arm_insn_reloc_data *data)
+arm_relocate_insn (uint32_t insn, struct arm_insn_reloc_visitor *visitor,
+		   struct arm_insn_reloc_data *data)
 {
   int err = 1;
 
   if ((insn & 0xf0000000) == 0xf0000000)
-    err = arm_decode_unconditional (insn, data);
+    err = arm_decode_unconditional (insn, visitor, data);
   else switch (((insn & 0x10) >> 4) | ((insn & 0xe000000) >> 24))
     {
     case 0x0: case 0x1: case 0x2: case 0x3:
-      err = arm_decode_dp_misc (insn, data);
+      err = arm_decode_dp_misc (insn, visitor, data);
       break;
 
     case 0x4: case 0x5: case 0x6:
-      err = arm_decode_ld_st_word_ubyte (insn, data);
+      err = arm_decode_ld_st_word_ubyte (insn, visitor, data);
       break;
 
     case 0x7:
-      err = arm_decode_media (insn, data);
+      err = arm_decode_media (insn, visitor, data);
       break;
 
     case 0x8: case 0x9: case 0xa: case 0xb:
-      err = arm_decode_b_bl_ldmstm (insn, data);
+      err = arm_decode_b_bl_ldmstm (insn, visitor, data);
       break;
 
     case 0xc: case 0xd: case 0xe: case 0xf:
-      err = arm_decode_svc_copro (insn, data);
+      err = arm_decode_svc_copro (insn, visitor, data);
       break;
     }
 
   return err;
 }
 
+static struct arm_insn_reloc_visitor arm_insn_reloc_visitor =
+{
+  arm_copy_alu_imm,
+  arm_copy_alu_reg,
+  arm_copy_alu_shifted_reg,
+  arm_copy_b_bl_blx,
+  arm_copy_block_xfer,
+  arm_copy_bx_blx_reg,
+  arm_copy_copro_load_store,
+  arm_copy_extra_ld_st,
+  arm_copy_ldr_str_ldrb_strb,
+  arm_copy_unmodified,
+  arm_copy_preload,
+  arm_copy_preload_reg,
+  arm_copy_svc,
+  arm_copy_undef,
+  arm_copy_unpred,
+};
+
 void
 arm_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
 			    CORE_ADDR to, struct regcache *regs,
@@ -7608,7 +7662,7 @@  arm_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
       dsc->is_thumb = 0;
       dsc->insn_size = 4;
 
-      err = arm_relocate_insn (insn, &reloc_data);
+      err = arm_relocate_insn (insn, &arm_insn_reloc_visitor, &reloc_data);
     }
   else
     {