[RFC,09/15] gdb: Add relocate instruction helpers

Message ID b3dfc6c9e6fdba064a0925c137723e63d3103362.1444820235.git.henrik.wallin@windriver.com
State New, archived
Headers

Commit Message

henrik.wallin@windriver.com Oct. 14, 2015, 11:14 a.m. UTC
  From: Henrik Wallin <henrik.wallin@windriver.com>

This adds the functions. No users yet.

The functions are used both when validating an instruction
when the users sets a fast tracepoint and when relocating
an instruction when gdbserver/ipa installs the jump pad.

Currently all PC relative instructions are considered
not relocatable.

Futher improvements can be made by rewriting some of those
instructions with alternative instructions.

gdb/ChangeLog:

	* arm-tdep.c : Add relocate functionality to be used by fast
	tracepoint support.

Signed-off-by: Henrik Wallin <henrik.wallin@windriver.com>
---
 gdb/arm-tdep.c | 599 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 599 insertions(+)
  

Comments

Yao Qi Oct. 27, 2015, 11:20 a.m. UTC | #1
henrik.wallin@windriver.com writes:

> The functions are used both when validating an instruction
> when the users sets a fast tracepoint and when relocating
> an instruction when gdbserver/ipa installs the jump pad.
>
> Currently all PC relative instructions are considered
> not relocatable.
>
> Futher improvements can be made by rewriting some of those
> instructions with alternative instructions.
>
> gdb/ChangeLog:
>
> 	* arm-tdep.c : Add relocate functionality to be used by fast
> 	tracepoint support.

You are relocating instructions in GDB side, which uses qRelocInsn
packet.  Why don't you relocate them in GDBserver side?  Search 
aarch64_relocate_instruction in gdbserver/linux-aarch64-low.c, and you
may have some clues from it.
  
henrik.wallin@windriver.com Oct. 29, 2015, 5:51 p.m. UTC | #2
2015-10-27 12:20 GMT+01:00 Yao Qi <qiyaoltc@gmail.com>:
> henrik.wallin@windriver.com writes:
>
>> The functions are used both when validating an instruction
>> when the users sets a fast tracepoint and when relocating
>> an instruction when gdbserver/ipa installs the jump pad.
>>
>> Currently all PC relative instructions are considered
>> not relocatable.
>>
>> Futher improvements can be made by rewriting some of those
>> instructions with alternative instructions.
>>
>> gdb/ChangeLog:
>>
>>       * arm-tdep.c : Add relocate functionality to be used by fast
>>       tracepoint support.
>
> You are relocating instructions in GDB side, which uses qRelocInsn
> packet.  Why don't you relocate them in GDBserver side?  Search
> aarch64_relocate_instruction in gdbserver/linux-aarch64-low.c, and you
> may have some clues from it.

Thanks for looking at the patches. I will check arm64 code and see if it can be moved to gdbserver side.

thanks,
/ Henrik
  
Simon Marchi Nov. 20, 2015, 6:06 p.m. UTC | #3
On 15-10-27 07:20 AM, Yao Qi wrote:
> henrik.wallin@windriver.com writes:
> 
>> The functions are used both when validating an instruction
>> when the users sets a fast tracepoint and when relocating
>> an instruction when gdbserver/ipa installs the jump pad.
>>
>> Currently all PC relative instructions are considered
>> not relocatable.
>>
>> Futher improvements can be made by rewriting some of those
>> instructions with alternative instructions.
>>
>> gdb/ChangeLog:
>>
>> 	* arm-tdep.c : Add relocate functionality to be used by fast
>> 	tracepoint support.
> 
> You are relocating instructions in GDB side, which uses qRelocInsn
> packet.  Why don't you relocate them in GDBserver side?  Search 
> aarch64_relocate_instruction in gdbserver/linux-aarch64-low.c, and you
> may have some clues from it.
> 

Hi Yao,

It seems like the code that Henrik adds in this patch (for relocating an
instruction) is very similar to the code currently in arm-tdep.c for
displaced stepping.  I think we can extract it and place it in arch/,
then use it both for displaced stepping and fast tracepoints.  That's
what aarch64 does currently, is that right?  Do you think it's the
right way to go for arm as well?

Simon
  
Yao Qi Nov. 23, 2015, 11 a.m. UTC | #4
Simon Marchi <simon.marchi@ericsson.com> writes:

> It seems like the code that Henrik adds in this patch (for relocating an
> instruction) is very similar to the code currently in arm-tdep.c for
> displaced stepping.  I think we can extract it and place it in arch/,
> then use it both for displaced stepping and fast tracepoints.  That's
> what aarch64 does currently, is that right?  Do you think it's the
> right way to go for arm as well?

Yes, I think the instruction decoding part in arm for instruction
relocation (for both fast tracepoint and displaced stepping) can be
shared.  The possibility of code sharing was considered at the beginning
of aarch64 work.  However, this possibility wasn't considered, AFICS,
when the arm displaced stepping was added, so I am afraid we need to
refactor, if not rewrite, the existing arm displaced stepping code a lot
for the purpose of sharing code.  It must take much effort.

On the other hand, GDBserver can't move threads out of jump pad without
hardware single step, which is a showstopper to ARM linux fast
tracepoint support.

In short, it is right to share code, but we need to figure out how to
move threads out of jump pad on ARM linux first.  Secondly, rewrite arm
displaced stepping code, and share decoding part with fast tracepoint
instruction relocation.  Unfortunately, the first one is still an open
question to me.
  
Simon Marchi Nov. 24, 2015, 4:29 p.m. UTC | #5
On 15-11-23 06:00 AM, Yao Qi wrote:
> Simon Marchi <simon.marchi@ericsson.com> writes:
> 
>> It seems like the code that Henrik adds in this patch (for relocating an
>> instruction) is very similar to the code currently in arm-tdep.c for
>> displaced stepping.  I think we can extract it and place it in arch/,
>> then use it both for displaced stepping and fast tracepoints.  That's
>> what aarch64 does currently, is that right?  Do you think it's the
>> right way to go for arm as well?
> 
> Yes, I think the instruction decoding part in arm for instruction
> relocation (for both fast tracepoint and displaced stepping) can be
> shared.  The possibility of code sharing was considered at the beginning
> of aarch64 work.  However, this possibility wasn't considered, AFICS,
> when the arm displaced stepping was added, so I am afraid we need to
> refactor, if not rewrite, the existing arm displaced stepping code a lot
> for the purpose of sharing code.  It must take much effort.

Ok, but if that's what it takes to get fast tracepoint support on ARM, we will
consider it.  It's certainly better than having a parallel implementation of
instruction decoding/relocation gdbserver-side.

> On the other hand, GDBserver can't move threads out of jump pad without
> hardware single step, which is a showstopper to ARM linux fast
> tracepoint support.

Ok, I see why that is important.  However, why is hardware single step
necessary?  With software single-step support added into gdbserver (thanks to
Antoine), isn't it possible to single step a thread out of the jump pad using
that?

> In short, it is right to share code, but we need to figure out how to
> move threads out of jump pad on ARM linux first.  Secondly, rewrite arm
> displaced stepping code, and share decoding part with fast tracepoint
> instruction relocation.  Unfortunately, the first one is still an open
> question to me.

If this problem can't be fixed directly, Antoine and I thought that we could
still have fast tracepoints support, but with known issues and some limitations,
such as not being able to instrument signal handlers, only allowing one tstart
for the life of the process (to avoid problems mentioned in stabilize_threads).

Could that be a good plan B?

Simon
  

Patch

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 4c99ddfe89cb..b277c3ef405c 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -9907,6 +9907,605 @@  arm_register_g_packet_guesses (struct gdbarch *gdbarch)
   /* Otherwise we don't have a useful guess.  */
 }
 
+/* Local structure to pass information in and out of the relocate
+   helper functions  */
+struct relocate_insn
+{
+  struct gdbarch *gdbarch;
+  CORE_ADDR *to;
+  CORE_ADDR oldloc;
+  enum bfd_endian byte_order_for_code;
+  int result; /* 0: copy unmodif, >0: handled, <0: not possible  */
+};
+
+static void
+append_insns (CORE_ADDR *to, ULONGEST len, uint32_t insn,
+	      enum bfd_endian byte_order)
+{
+  write_memory_unsigned_integer (*to, len, byte_order, insn);
+  *to += len;
+}
+
+static void
+arm_relocate_insn_arm (struct relocate_insn *rel, uint32_t insn)
+{
+  unsigned int cond = bits (insn, 28, 31);
+  unsigned int op   = (bits (insn, 25, 27) << 1) | bit (insn, 4);
+
+  /* 1111 ---- ---- ---- ---- ---- ---- ---- :
+     unconditional instructions  */
+  if (cond == 15)
+    {
+      /* 1111 0--- ---- ---- ---- ---- ---- ---- :
+	 Memory hints, Advanced SIMD instructions, and
+	 miscellaneous instructions  */
+      if (bit (insn, 27) == 0)
+	{
+	  unsigned int op1 = bits (insn, 20, 26);
+	  unsigned int op2 = bits (insn, 4, 7);
+	  unsigned int rn = bits (insn, 16, 19);
+
+	  /* All variants we are interested in have rn == 15 - check first  */
+	  if (rn == 15)
+	    {
+	      /* 1111 0100 x101 1111 ---- ---- ---- ---- :
+		 PLI (literal)  */
+	      if ((op1 & 0x77) == 0x45)
+		rel->result = -1;
+	      /* 1111 0101 x101 1111 ---- ---- ---- ---- :
+		 PLD (literal)  */
+	      else if ((op1 & 0x77) == 0x55)
+		rel->result = -1;
+	      /* 1111 0110 x101 1111 ---- ---- ---0 ---- :
+		 PLI (register)  */
+	      else if ((op1 & 0x77) == 0x65 && (op2 & 0x1) == 0x0)
+		rel->result = -1;
+	      /* 1111 0111 xx01 1111 ---- ---- ---0 ---- :
+		 PLD, PLDW (register)  */
+	      else if ((op1 & 0x73) == 0x71 && (op2 & 0x1) == 0x0)
+		rel->result = -1;
+	    }
+	}
+      /* 1111 101- ---- ---- ---- ---- ---- ---- :
+	 BL, BLX (immediate)  */
+      else if (bits (insn, 25, 27) == 5)
+	rel->result = -1;
+      /* 1111 110x xxxx ---- ---- ---- ---- ---- :
+	 STC/STC2, LDC/LDC2 ( x != 00x0x)  */
+      else if (bits (insn, 25, 27) == 6 &&
+	       (bits (insn, 20, 24) & 0x1A) != 0x0)
+	{
+	  unsigned int rn = bits (insn, 16, 19);
+	  if (rn == 15)
+	    rel->result = -1;
+	}
+    }
+
+  /* ---- 00x- ---- ---- ---- ---- ---x ---- :
+     Data-processing and miscellaneous instructions  */
+  else if (op <= 3)
+    {
+      unsigned int op1 = bits (insn, 20, 24);
+      unsigned int op2 = bits (insn, 4, 7);
+
+      if (bit (insn, 25) == 0)
+	{
+	  /* ---- 000x xxxx ---- ---- ---- xxx0 ---- :
+	     Data-processing (register) ( x != 10xx0 )  */
+	  if ((op1 & 0x19) != 0x10 && (op2 & 0x1) == 0x0) {
+	    unsigned int rm = bits (insn, 0, 3);
+	    unsigned int rn = bits (insn, 16, 19);
+
+	    /* ---- 000x xxxx nnnn ---- ---- xxx0 mmmm :
+	       AND,EOR,SUB,RSB,ADD,ADC,SBC,RSC,TST,TEQ,
+	       CMP,CMN,ORR,MOV,LSL,LSR,ASR,RRX,ROR,BIC,MVN  */
+	    if (rn == 15 || rm == 15)
+	      rel->result = -1;
+	  }
+	  /* ---- 000x xxxx ---- ---- ---- xxx1 ---- :
+	     Data-processing (register-shifted register)  */
+	  else if ((op1 & 0x19) != 0x10 && (op2 & 0x9) == 0x1)
+	    {
+	      ;
+	    }
+
+	  /* ---- 0001 0xx0 ---- ---- ---- 0xxx ---- :
+	     Miscellaneous instructions  */
+	  else if ((op1 & 0x19) == 0x10 && (op2 & 0x8) == 0x0)
+	    {
+	      /* ---- 0001 0010 ---- ---- ---- 0011 ---- :
+		 BLX (register)  */
+	      if (bits (insn, 4, 6) == 3 && bits (insn, 21, 22) == 1) /* BLX  */
+		rel->result = -1;
+	    }
+	  /* ---- 0001 0xx0 ---- ---- ---- 1xx0 ---- :
+	     Halfword multiply and multiply accumulate  */
+	  else if ((op1 & 0x19) == 0x10 && (op2 & 0x9) == 0x8)
+	    {
+	      ;
+	    }
+	  /* ---- 0000 xxxx ---- ---- ---- 1001 ---- :
+	     Multiply and multiply accumulate  */
+	  else if ((op1 & 0x10) == 0x00 && op2 == 0x9)
+	    {
+	      ;
+	    }
+	  /* ---- 0001 xxxx ---- ---- ---- 1001 ---- :
+	     Synchronization primitives  */
+	  else if ((op1 & 0x10) == 0x10 && op2 == 0x9)
+	    {
+	      ;
+	    }
+	  /* ---- 000x xxxx ---- ---- ---- 1xx1 ---- :
+	     Extra load/store instructions  */
+	  else if (op2 == 0xB || (op2 & 0xd) == 0xd)
+	    {
+	      unsigned int rn = bits (insn, 16, 19);
+	      /* ---- 000x xxxx 1111 ---- ---- 1xx1 ---- :
+		 STRH, LDRH, LDRD, LDRSB  */
+	      if (rn == 15)
+		rel->result = -1;
+	    }
+	}
+      else
+	{
+	  /* ---- 001x xxxx ---- ---- ---- ---- ---- :
+	     Data-processing (immediate)  */
+	  if ((op1 & 0x19) != 0x10)
+	    {
+	      unsigned int op  = bits (insn, 21, 24);
+	      unsigned int op2 = bits (insn, 20, 24);
+	      unsigned int rn = bits (insn, 16, 19);
+
+	      if (rn == 15) /* Only check those with n == 15  */
+		{
+		  /* ---- 0010 xxxx 1111 ---- ---- ---- ---- :
+		     AND, EOR, ADR, RSB, ADR, ADC, SBC, RSC  */
+		  if (op <= 7)
+		    rel->result = -1;
+		  /* ---- 0011 0xx1 1111 ---- ---- ---- ---- :
+		     TST, TEQ, CMP, CMN  */
+		  else if ((op2 & 0x19) == 0x11)
+		    rel->result = -1;
+		  /* ---- 0011 1x0x 1111 ---- ---- ---- ---- :
+		     ORR, BIC  */
+		  else if (op == 0xC || op == 0xE)
+		    rel->result = -1;
+		  /* ---- 0011 1x1x 1111 ---- ---- ---- ---- :
+		     MOV, MVN  */
+		  else if (op == 0xD || op == 0xF)
+		    {
+		      ;
+		    }
+		}
+	    }
+	}
+    }
+
+  /* ---- 01A- ---- ---- ---- ---- ---B ---- :
+     Load/store word and unsigned byte  */
+  else if (op <= 6)
+    {
+      unsigned int a = bit (insn, 25);
+      unsigned int rt = bits (insn, 12, 15);
+      unsigned int rn = bits (insn, 16, 19);
+      unsigned int op1 = bits (insn, 20, 24);
+      unsigned int op1_m1 = (op1 & 0x17);
+      unsigned int op1_m2 = (op1 & 0x5);
+
+      /* a and b can not both be 1 - no need to test for b  */
+
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 STR (immediate), STR (register)  */
+      if (op1_m2 == 0x00 && op1_m1 != 0x02)
+	{
+	  if (rt == 15 || rn == 15)
+	    rel->result = -1;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 STRT  */
+      else if (op1_m1 == 0x02)
+	{
+	  if (rt == 15)
+	    rel->result = -1;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 LDR (literal)  */
+      else if (op1_m2 == 0x01 && op1_m1 != 0x03)
+	{
+	  if (rn == 15)
+	    rel->result = -1;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 LDRT  */
+      else if (op1_m1 == 0x03)
+	{
+	  ;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 STRB (immediate), STRB (register)  */
+      else if (op1_m2 == 0x04 && op1_m1 != 0x06)
+	{
+	  if (rn == 15)
+	    rel->result = -1;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 STRBT  */
+      else if (op1_m1 == 0x06)
+	{
+	  ;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 LDRB (immediate), LDRB (register)  */
+      else if (op1_m2 == 0x05 && op1_m1 != 0x07)
+	{
+	  if (rn == 15)
+	    rel->result = -1;
+	}
+      /* ---- 01Ax xxxx nnnn ---- ---- ---B ---- :
+	 LDRBT  */
+      else if (op1_m1 == 0x07)
+	{
+	  ;
+	}
+    }
+
+  /* ---- 011- ---- ---- ---- ---- ---1 ---- :
+     Media instructions  */
+  else if (op <= 7)
+    {
+      ;
+    }
+
+  /* ---- 10x- ---- ---- ---- ---- ---x ---- :
+     Branch, branch with link, and block data transfer  */
+  else if (op <= 11)
+    {
+      unsigned int op1 = bits (insn, 20, 24);
+      unsigned int r = bit (insn, 15);
+      unsigned int rn = bits (insn, 16, 19);
+
+      /* ---- 101x xxxx nnnn r--- ---- ---- ---- :
+	 B, BL, BLX  */
+      if (bit (insn, 25))
+	rel->result = -1;
+      /* ---- 100x xxx0 nnnn r--- ---- ---- ---- :
+	 STMDA, STM, STMDB, STMIB  */
+      else if (bit (insn, 20) == 0)
+	{
+	  if (rn == 15 || r == 1)
+	    rel->result = -1;
+	}
+      /* ---- 100x xxx1 nnnn r--- ---- ---- ---- :
+	 LDMDA, LDM/LDMIALDMFD, LDMDB/LDMEA, LSMIB/LDMED  */
+      else
+	{
+	  ;
+	}
+    }
+
+  /* ---- 11x- ---- ---- ---- ---- ---x ---- :
+     Coprocessor instructions, and Supervisor Call  */
+  else
+    {
+      unsigned int op1 = bits (insn, 20, 25);
+      unsigned int rn = bits (insn, 16, 19);
+      unsigned int coproc = bits (insn, 8, 11);
+      unsigned int op = bit (insn, 4);
+
+      /* ---- 1100 000x nnnn ---- xxxx ---x ---- :
+	 undefined  */
+      /* ---- 1111 xxxx nnnn ---- xxxx ---x ---- :
+	 SVC  */
+      if ((op1 & 0x3E) == 0 || (op1 & 0x30) == 0x30)
+	{
+	  ;
+	}
+      else if ((coproc & 0xE) != 0xA)
+	{
+	  /* ---- 1100 010x nnnn ---- xxxx ---x ---- :
+	     MCRR/MCRR2, MRRC/MRRC2  */
+	  if (op1 == 4 || op1 == 5)
+	    {
+	      ;
+	    }
+	  /* ---- 110x xxxx nnnn ---- xxxx ---x ---- :
+	     STC/STC2, LDC/LDC2  */
+	  else if ((op1 & 0x20) == 0)
+	    {
+	      if (rn == 15)
+		rel->result = -1;
+	    }
+	  /* ---- 1110 xxxx nnnn ---- xxxx ---x ---- :
+	     CDP/CDP2, MCR/MCR2, MRC/MRC2  */
+	  else if ((op1 & 0x30) == 0x20)
+	    {
+	      ;
+	    }
+	}
+      else
+	{
+	  /* ---- 110x xxxx nnnn ---- 101x ---x ---- :
+	     Extension register load/store instructions  */
+	  if ((op1 & 0x20) == 0 && (op1 & 0x3A) != 0)
+	    {
+	      /* ---- 110x xxxx 1111 ---- 101- ---- ---- :
+		 VSTM, VSTR, VLDM, VLDR  */
+	      if (rn == 15)
+		rel->result = -1;
+	    }
+	}
+    }
+
+  if (rel->to && rel->result == 0)
+    {
+      append_insns (rel->to, 4, insn, rel->byte_order_for_code);
+    }
+}
+
+static void
+arm_relocate_insn_thumb16 (struct relocate_insn *rel, uint16_t insn)
+{
+  unsigned short op = bits (insn, 10, 15);
+
+  /* 0100 01-- ---- ---- :
+     Special data instructions and branch and exchange.  */
+  if (op == 0x11)
+    {
+      /* 0100 0111 1x-- ---- :
+	 BLX (register)  */
+      if (bits (insn, 7, 9) == 7)
+	rel->result = -1;
+      /* 0100 01xx xx-- ---- :
+	 ADD/MOV/CMP high registers.  */
+      else if (bits (insn, 6, 7) != 0)
+	{
+	  if (bits (insn, 3, 6) == 15 ||
+	      ((bit (insn, 7) << 3) | bits (insn, 0, 2)) == 15)
+	    rel->result = -1;
+	}
+    }
+  /* 0100 1x-- ---- ---- :
+     LDR (literal)  */
+  else if ((op & 0x3E) == 0x12)
+    rel->result = -1;
+  /* 1010 0x-- ---- ---- :
+     Generate PC-relative address (ADR)  */
+  else if ((op & 0x3E) == 0x28)
+    rel->result = -1;
+  /* 1011 xx-- ---- ---- :
+     Misc 16-bit instructions  */
+  else if ((op & 0x3C) == 0x2C)
+    {
+      unsigned short op2 = bits (insn, 8, 11);
+      /* 1011 x0x1 ---- ---- :
+	 CBNZ, CBZ  */
+      if ((op2 & 0x5) == 0x1)
+	rel->result = -1;
+      /* 1011 1111 ---- xxxx :
+	 IT  */
+      else if (op2 == 0xF && bits (insn, 0, 3) != 0)
+	rel->result = -1;
+    }
+  /* 1101 xx-- ---- ---- :
+     Conditional branch and supervisor calls  */
+  else if ((op & 0x3C) == 0x34) /*   */
+    {
+      /* 1101 xxxx ---- ---- :
+	 B (conditional)  */
+      if (bits (insn, 9, 11) != 7)
+	rel->result = -1;
+    }
+  /* 1110 0x-- ---- ---- :
+     B (unconditional)  */
+  else if ((op & 0x3E) == 0x38)
+    rel->result = -1;
+
+  if (rel->to && rel->result == 0)
+    append_insns (rel->to, 2, insn, rel->byte_order_for_code);
+}
+
+static void
+arm_relocate_insn_thumb32 (struct relocate_insn *rel,
+			   uint16_t insn1, uint16_t insn2)
+{
+  unsigned int op1 = bits (insn1, 11, 12);
+  unsigned int op2 = bits (insn1, 4, 10);
+  unsigned short op = bit (insn2, 15);
+
+  if (op1 == 1)
+    {
+      /* 1110 100x x1xx ---- x--- ---- ---- ---- :
+	 Load/store dual, load/store excl, table branch  */
+      if ((op2 & 0x64) == 0x04) /*   */
+	{
+	  unsigned int op1 = bits (insn1, 7, 8);
+	  unsigned int op2 = bits (insn1, 4, 5);
+	  /* 1110 1000 1101 ---- ---- ---- 000- ---- :
+	     TBB, TBH  */
+	  if (op1 == 1 && op2 == 1 && bits (insn2, 5, 7) == 0)
+	    rel->result = -1;
+	  /* 1110 1000 x111 1111 ---- ---- ---- ---- :
+	     LDRD (literal)  */
+	  /* 1110 1001 x1x1 1111 ---- ---- ---- ---- :
+	     LDRD (literal)  */
+	  else if (bits (insn1, 0, 3) == 15)
+	    {
+	      if (((op1 & 0x2) == 0 && op2 == 3) ||
+		  ((op1 & 0x2) == 2 && (op2 & 1) == 1))
+		rel->result = -1;
+	    }
+	}
+      /* 1110 11xx xxxx ---- x--- ---- ---- ---- :
+	 Coprocessor, Advanced SIMD, and Floating-point instructions  */
+      else if ((op2 & 0x40) == 0x40)
+	{
+	  unsigned int op3 = bits (insn1, 4, 9);
+	  unsigned int coproc = bits (insn2, 9, 11);
+	  /* 1110 1101 xx01 nnnn ---- 101x ---x ---- :
+	     Extension register load/store instructions / VLDR  */
+	  if (coproc == 5 && (op3 & 0x33) == 0x11)
+	    rel->result = -1;
+	  /* 1110 110x xxx1 nnnn ---- xxxx ---x ---- :
+	     LDC/LDC2 (literal)  */
+	  else if (coproc != 5 && (op3 & 0x21) == 1 &&
+		   (op3 & 0x3A) != 0 && bits (insn1, 0, 3) == 0xF)
+	    rel->result = -1;
+	}
+    }
+
+  else if (op1 == 2)
+    {
+      /* 1111 0xxx xxxx ---- 1--- ---- ---- ---- :
+	 Branches and miscellaneous control  */
+      if (op)
+	{
+	  /* 1111 0xxx xxxx ---- 11xx xxxx ---- ---- :
+	     BL/BLX  */
+	  if (bit (insn2, 14))
+	    rel->result = -1;
+	  /* 1111 0xxx xxxx ---- 10x1 xxxx ---- ---- :
+	     B (unconditional)  */
+	  else if (bit (insn2, 12))
+	    rel->result = -1;
+	  /* 1111 0xxx xxxx ---- 10x0 xxxx ---- ---- :
+	     B (conditional)  */
+	  else if (bits (insn1, 7, 9) != 0x7)
+	    rel->result = -1;
+	}
+      /* 1111 0x1x xxxx ---- 0--- ---- ---- ---- :
+	 Data processing (plain binary immediate)  */
+      else if (bit (insn1, 9))
+	{
+	  int op = bits (insn1, 4, 8);
+	  int rn = bits (insn1, 0, 3);
+	  /* 1111 0x1x x0x0 1111 0--- ---- ---- ---- :
+	     ADR  */
+	  if ((op == 0 || op == 0xa) && rn == 0xf)
+	    rel->result = -1;
+	}
+    }
+
+  else
+    {
+      /* 1111 100x x001 ---- x--- ---- ---- ---- :
+	 Load byte, memory hints  */
+      if ((op2 & 0x67) == 0x1)
+	{
+	  /* 1111 100x x001 nnnn tttt xxxx xx-- ---- :
+	     LDRB (literal), PLD (literal),
+	     LDRSB (literal), PLI (immediate, literal)  */
+	  if (bits (insn1, 0, 3) == 15)
+	    rel->result = -1;
+	}
+      /* 1111 100x x011 ---- x--- ---- ---- ---- :
+	 Load halfword, memory hints  */
+      else if ((op2 & 0x67) == 0x3)
+	{
+	  /* 1111 100x x011 nnnn tttt xxxx xx-- ---- :
+	     LDRH (literal), LDRSH (literal)  */
+	  if (bits (insn1, 0, 3) == 15 && bits (insn2, 12, 15) != 15)
+	    rel->result = -1;
+	}
+      /* 1111 100x x101 ---- x--- ---- ---- ---- :
+	 Load word  */
+      else if ((op2 & 0x67) == 0x5)
+	{
+	  int rt = bits (insn2, 12, 15);
+	  int rn = bits (insn1, 0, 3);
+	  /* 1111 100x x101 1111 ---- xxxx xx-- ---- :
+	     LDR (literal)  */
+	  if (rn == 15)
+	    rel->result = -1;
+	}
+      /* 1111 11xx xxxx ---- x--- ---- ---- ---- :
+	 Coprocessor, Advanced SIMD, and Floating-point instructions  */
+      else if ((op2 & 0x40) == 0x40)
+	{
+	  unsigned int op1_ = bits (insn1, 4, 9);
+
+	  /* 1111 110x xxx1 1111 ---- xxxx ---x ---- :
+	     LDC, LDC2 (literal)  */
+	  if ((bits (insn2, 8, 11) & 0xE) != 0xA &&
+	      (op1_ & 0x21) == 0x01 && (op1_ & 0x3A) != 0 &&
+	      bits (insn1, 0, 3) == 15)
+	    rel->result = -1;
+	}
+    }
+
+  if (rel->to && rel->result == 0)
+    {
+      append_insns (rel->to, 2, insn1, rel->byte_order_for_code);
+      append_insns (rel->to, 2, insn2, rel->byte_order_for_code);
+    }
+}
+
+static void
+arm_relocate_instruction_func (struct relocate_insn *rel)
+{
+  rel->byte_order_for_code = gdbarch_byte_order_for_code (rel->gdbarch);
+  rel->result = 0;
+
+  if (arm_pc_is_thumb (rel->gdbarch, rel->oldloc))
+    {
+      uint16_t insn1;
+      uint16_t insn2;
+
+      insn1 = read_memory_unsigned_integer (rel->oldloc, 2,
+					    rel->byte_order_for_code);
+      if (thumb_insn_size (insn1) == 2)
+	arm_relocate_insn_thumb16 (rel, insn1);
+      else
+	{
+	  insn2 = read_memory_unsigned_integer (rel->oldloc + 2, 2,
+						rel->byte_order_for_code);
+	  arm_relocate_insn_thumb32 (rel, insn1, insn2);
+	}
+    }
+  else
+    {
+      uint32_t insn;
+
+      insn = read_memory_unsigned_integer (rel->oldloc, 4,
+					   rel->byte_order_for_code);
+      arm_relocate_insn_arm (rel, insn);
+    }
+}
+
+static int
+arm_check_relocate_instruction (struct gdbarch *gdbarch,
+				CORE_ADDR oldloc, char **msg)
+{
+  struct relocate_insn rel;
+
+  rel.gdbarch = gdbarch;
+  rel.to = NULL;
+  rel.oldloc = oldloc;
+
+  arm_relocate_instruction_func (&rel);
+
+  if (rel.result == -1)
+    {
+      if (msg)
+	*msg = xstrprintf (_("; instruction cannot be relocated"));
+    }
+
+  return rel.result;
+}
+
+static void
+arm_relocate_instruction (struct gdbarch *gdbarch,
+			  CORE_ADDR *to, CORE_ADDR oldloc)
+{
+  struct relocate_insn rel;
+
+  rel.gdbarch = gdbarch;
+  rel.to = to;
+  rel.oldloc = oldloc;
+
+  arm_relocate_instruction_func (&rel);
+}
+
 
 /* Initialize the current architecture based on INFO.  If possible,
    re-use an architecture from ARCHES, which is a list of