diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 1853e79bc140..6bb069fe71bb 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -926,6 +926,21 @@ is_target_arm (CORE_ADDR to, CORE_ADDR from)
   return (to == ptr) ? 0 : 1;
 }
 
+static void
+append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf)
+{
+  write_inferior_memory (*to, buf, len);
+  *to += len;
+}
+
+static int
+copy_instruction (CORE_ADDR *to, CORE_ADDR from, size_t len)
+{
+  CORE_ADDR before = *to;
+  relocate_instruction (to, from);
+  return (before == *to) ? -1 : 1;
+}
+
 static uint32_t
 mk_t_b_rel (CORE_ADDR from, CORE_ADDR to)
 {
@@ -1060,6 +1075,220 @@ mk_a_load_instr (uint32_t *mem, int reg, uint32_t val)
   return mem;
 }
 
+static int
+arm_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint,
+				      CORE_ADDR tpaddr,
+				      CORE_ADDR collector,
+				      CORE_ADDR lockaddr,
+				      ULONGEST orig_size,
+				      CORE_ADDR *jump_entry,
+				      CORE_ADDR *trampoline,
+				      ULONGEST *trampoline_size,
+				      unsigned char *jjump_pad_insn,
+				      ULONGEST *jjump_pad_insn_size,
+				      CORE_ADDR *adjusted_insn_addr,
+				      CORE_ADDR *adjusted_insn_addr_end,
+				      char *err)
+{
+  unsigned char buf[0x100];
+  CORE_ADDR buildaddr = *jump_entry;
+
+  /* Register usage:
+     r0 - arg1:tpoint   /   tmp
+     r1 - arg2:sp
+     r2 - collector
+     r3 - tmp
+     r4 - lockaddr
+     r5 - saved sp      /   collector_t   */
+
+  if (is_target_arm(buildaddr, tpaddr))	/* arm mode  */
+    {
+      uint32_t *ptr = (uint32_t *) buf;
+
+      *ptr++ = 0xe92d5fff;            /* push { r0-r12,lr }  */
+      *ptr++ = 0xe10f0000;            /* mrs r0,cpsr  */
+      *ptr++ = 0xe52d0004;            /* push r0  */
+
+      ptr = mk_a_load_instr (ptr, 0, (uint32_t) tpaddr);
+      *ptr++ = 0xe52d0004;            /* push r0 (orig pc)  */
+      *ptr++ = 0xe1a0100d;            /* mov r1, sp (regs:arg2)  */
+
+      ptr = mk_a_load_instr (ptr, 0, 0xffff0fe0);
+      ptr = mk_a_blx_instr (ptr, 0);
+      *ptr++ = 0xe52d0004;            /* push r0 (tls)  */
+
+      ptr = mk_a_load_instr (ptr, 0, (uint32_t) tpoint);
+      *ptr++ = 0xe52d0004;            /* push r0 (tpoint:arg1)  */
+      ptr = mk_a_load_instr (ptr, 2, (uint32_t) collector);
+      ptr = mk_a_load_instr (ptr, 4, (uint32_t) lockaddr);
+
+      *ptr++ = 0xe1a0500d;            /*    mov r5, sp  */
+      *ptr++ = 0xf57ff05f;            /* 1: dmb sy  */
+      *ptr++ = 0xe1943f9f;            /* 2: ldrex r3, [r4]  */
+      *ptr++ = 0xe3530000;            /*    cmp r3, #0  */
+      *ptr++ = 0x1a000002;            /*    bne 3  */
+      *ptr++ = 0xe184ef95;            /*    strex r14, r5, [r4]  */
+      *ptr++ = 0xe35e0000;            /*    cmp r14, #0  */
+      *ptr++ = 0x1afffff9;            /*    bne 2  */
+      *ptr++ = 0xf57ff05f;            /* 3: dmb sy  */
+      *ptr++ = 0x1afffff6;            /*    bne 1  */
+
+      *ptr++ = 0xe3c53007;            /* bic r3, r5, 7  */
+      *ptr++ = 0xe1a0d003;            /* mov sp, r3  */
+      ptr = mk_a_blx_instr (ptr, 2);
+      *ptr++ = 0xe1a0d005;            /* mov sp, r5  */
+
+      *ptr++ = 0xe3a03000;            /* mov r3, #0  */
+      *ptr++ = 0xe5843000;            /* str r3, [r4]  */
+
+      *ptr++ = 0xe28dd00c;            /* add sp, sp, #12  */
+      *ptr++ = 0xe49d0004;            /* pop r0  */
+      *ptr++ = 0xe12cf000;            /* msr cpsr,r0  */
+      *ptr++ = 0xe8bd5fff;            /* pop { r0-r12,lr }  */
+
+      append_insns (&buildaddr, (uint32_t) ptr - (uint32_t) buf, buf);
+
+      *adjusted_insn_addr = buildaddr;
+      if (copy_instruction (&buildaddr, tpaddr, 4) < 0)
+	{
+	  strcpy (err, "E.Cannot move instruction to jump_pad. "
+		  "Not possible to relocate.");
+	  return 1;
+	}
+      *adjusted_insn_addr_end = buildaddr;
+
+      /* Possible improvements:
+	 This branch can be made non-relative:
+	 B <mem location>:
+	 push    {r0,r1}
+	 movw    r0, #<mem location>
+	 movt    r0, #<mem location>
+	 str     r0, [sp, #4]
+	 pop     {r0,pc}  */
+      if (!mk_a_b_isreachable (buildaddr, tpaddr + 4))
+	{
+	  strcpy (err, "E.Cannot construct valid branch "
+		  "instruction for fast tracepoint."
+		  " Too long relative branch.");
+	  return 1;
+	}
+      /* b <tp_addr + 4>  */
+      (void) mk_a_b_instr ((uint32_t *) buf, buildaddr, tpaddr + 4);
+      append_insns (&buildaddr, 4, buf);
+
+      /* write tp instr.  */
+      if (!mk_a_b_isreachable (tpaddr, *jump_entry))
+	{
+	  strcpy (err, "E.Cannot construct valid branch "
+		  "instruction for fast tracepoint."
+		  " Too long relative branch.");
+	  return 1;
+	}
+      (void) mk_a_b_instr ((uint32_t *) jjump_pad_insn, tpaddr, *jump_entry);
+      *jjump_pad_insn_size = 4;
+      *jump_entry = buildaddr;
+    }
+  else                        /* thumb mode  */
+    {
+      uint16_t *ptr = (uint16_t *) buf;
+
+      *ptr++ = 0xe92d;            /* push { r0-r12,lr }  */
+      *ptr++ = 0x5fff;
+      *ptr++ = 0xf3ef;            /* mrs r0,cpsr  */
+      *ptr++ = 0x8000;
+      *ptr++ = 0xb401;            /* push r0  */
+
+      ptr = mk_t_load_instr (ptr, 0, (uint32_t) tpaddr);
+      *ptr++ = 0xb401;            /* push r0 (orig pc)  */
+      *ptr++ = 0x4669;            /* mov r1, sp (regs:arg2)  */
+
+      ptr = mk_t_load_instr (ptr, 0, 0xffff0fe0);
+      ptr = mk_t_blx_instr (ptr, 0);
+      *ptr++ = 0xb401;		/* push r0 (tls)  */
+
+      ptr = mk_t_load_instr (ptr, 0, (uint32_t) tpoint);
+      *ptr++ = 0xb401;		/* push r0 (tpoint:arg1)  */
+      ptr = mk_t_load_instr (ptr, 2, (uint32_t) collector);
+      ptr = mk_t_load_instr (ptr, 4, (uint32_t) lockaddr);
+
+      *ptr++ = 0x466d;		/*    mov r5, sp  */
+      *ptr++ = 0xf3bf;		/* 1: dmb sy  */
+      *ptr++ = 0x8f5f;
+      *ptr++ = 0xe854;		/* 2: ldrex   r3, [r4]	*/
+      *ptr++ = 0x3f00;
+      *ptr++ = 0x2b00;		/*    cmp     r3, #0  */
+      *ptr++ = 0xd104;		/*    bne.n   3	 */
+      *ptr++ = 0xe844;		/*    strex   r14, r5, [r4]  */
+      *ptr++ = 0x5e00;
+      *ptr++ = 0xf1be;		/*    cmp.w   r14, #0  */
+      *ptr++ = 0x0f00;
+      *ptr++ = 0xd1f6;		/*    bne.n   2	 */
+      *ptr++ = 0xf3bf;		/* 3. dmb  sy  */
+      *ptr++ = 0x8f5f;
+      *ptr++ = 0xd1f1;		/*    bne.n   1	 */
+
+      *ptr++ = 0xf025;		/* bic r3, r5, 7  */
+      *ptr++ = 0x0307;
+      *ptr++ = 0x469d;		/* mov sp, r3  */
+      ptr = mk_t_blx_instr (ptr, 2);
+      *ptr++ = 0x46ad;		/* mov sp, r5  */
+
+      *ptr++ = 0xf04f;		/* mov r3, #0  */
+      *ptr++ = 0x0300;
+      *ptr++ = 0x6023;		/* str r3, [r4]	 */
+
+      *ptr++ = 0xb003;		/* add sp, #12	*/
+      *ptr++ = 0xbc01;		/* pop r0  */
+      *ptr++ = 0xf380;		/* msr cpsr,r0	*/
+      *ptr++ = 0x8c00;
+      *ptr++ = 0xe8bd;		/* pop { r0-r12,lr }  */
+      *ptr++ = 0x5fff;
+
+      append_insns (&buildaddr, (uint32_t) ptr - (uint32_t) buf, buf);
+
+      *adjusted_insn_addr = buildaddr;
+      if (copy_instruction (&buildaddr, tpaddr, 4) < 0)
+	{
+	  strcpy (err, "E.Cannot move instruction to jump_pad."
+		  " Not possible to relocate.");
+	  return 1;
+	}
+      *adjusted_insn_addr_end = buildaddr;
+
+      /* Possible improvements:
+	 This branch can be made non-relative:
+	 B <mem location>:
+	 push	   {r0,r1}
+	 movw	   r0, #<mem location>
+	 movt	   r0, #<mem location>
+	 str	   r0, [sp, #4]
+	 pop	   {r0,pc}  */
+      if (!mk_t_b_isreachable (buildaddr, tpaddr + 4))
+	{
+	  strcpy (err, "E.Cannot construct valid branch "
+		  "instruction for fast tracepoint."
+		  "Too long relative branch.");
+	  return 1;
+	}
+      (void) mk_t_b_instr ((uint16_t *) buf, buildaddr, tpaddr + 4);
+      append_insns (&buildaddr, 4, buf);
+
+      /* write tp instr.	*/
+      if (!mk_t_b_isreachable (tpaddr, *jump_entry))
+	{
+	  strcpy (err, "E.Cannot construct valid branch "
+		  "instruction for fast tracepoint."
+		  "Too long relative branch.");
+	  return 1;
+	}
+      (void) mk_t_b_instr ((uint16_t *) jjump_pad_insn, tpaddr, *jump_entry);
+      *jjump_pad_insn_size = 4;
+      *jump_entry = buildaddr;
+    }
+
+  return 0;
+}
+
 struct linux_target_ops the_low_target = {
   arm_arch_setup,
   arm_regs_info,
