diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 4b0e567..2e66caa 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -1053,11 +1053,24 @@ struct arm_insn_reloc_data
     uint16_t thumb32[2];
   } insns;
 
+  /* The original PC of the instruction.  */
+  CORE_ADDR orig_loc;
+
+  /* The PC where the modified instruction(s) will start.  */
+  CORE_ADDR new_loc;
+
   /* Error message to return to the client in case the relocation fails.  */
   const char *err;
 };
 
 static int
+arm_reloc_refs_pc_error (struct arm_insn_reloc_data *data)
+{
+  data->err = "The instruction references the PC, couldn't relocate.";
+  return 1;
+}
+
+static int
 arm_reloc_others (uint32_t insn, const char *iname,
 		  struct arm_insn_reloc_data *data)
 {
@@ -1069,86 +1082,168 @@ arm_reloc_others (uint32_t insn, const char *iname,
 static int
 arm_reloc_alu_imm (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000f0000ul))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "ALU immediate", data);
 }
 
 static int
 arm_reloc_alu_reg (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000ff00ful))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "ALU reg", data);
 }
 
 static int
 arm_reloc_alu_shifted_reg (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000fff0ful))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "ALU shifted reg", data);
 }
 
 static int
 arm_reloc_b_bl_blx (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  uint32_t cond = bits (insn, 28, 31);
+  int exchange = (cond == 0xf);
+
+  if (exchange)
+    {
+      /* blx */
+      CORE_ADDR absolute_dest = BranchDest (data->orig_loc, insn);
+
+      /* Adjust the address if the H bit is set.  */
+      if (bit(insn, 24))
+	absolute_dest |= (1 << 1);
+      arm_emit_arm_blx
+	(&data->insns.arm, cond,
+	 immediate_operand (arm_arm_branch_relative_distance (data->new_loc,
+							      absolute_dest)));
+    }
+  else
+    {
+      /* b or bl */
+      CORE_ADDR absolute_dest = BranchDest (data->orig_loc, insn);
+      int link = exchange || bit (insn, 24);
+
+      if (link)
+	{
+	  arm_emit_arm_bl (&data->insns.arm, cond,
+			   arm_arm_branch_relative_distance (data->new_loc,
+							     absolute_dest));
+	}
+      else
+	{
+	  arm_emit_arm_b (&data->insns.arm, cond,
+			  arm_arm_branch_relative_distance (data->new_loc,
+							    absolute_dest));
+	}
+    }
+
+  return 0;
 }
 
 static int
 arm_reloc_block_xfer (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (bit (insn, 20) == 1)
+    {
+      /* it's an LDM/POP-kind instruction.  If it mentions PC, the instruction
+         will result in a jump.  The destination address is absolute, so it
+         won't change the result if we execute it out of line.  */
+      return arm_reloc_others (insn, "ldm", data);
+    }
+  else
+    {
+      /* It's an STM/PUSH-kind instruction.  If it mentions PC, we will store
+         the wrong value of PC in memory.  Therefore, error out if PC is
+         mentioned.  */
+      if ((insn & (1 << 15)) != 0)
+	return arm_reloc_refs_pc_error (data);
+
+      return arm_reloc_others (insn, "stm", data);
+    }
 }
 
 static int
 arm_reloc_bx_blx_reg (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  /* These instructions take an absolute address in a register, so there's
+     nothing special to do.  */
+  return arm_reloc_others(insn, "bx/blx", data);
 }
 
 static int
 arm_reloc_copro_load_store (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000f0000ul))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "copro load/store", data);
 }
 
 static int
 arm_reloc_extra_ld_st (uint32_t insn, struct arm_insn_reloc_data *data,
 		       int unprivileged)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000ff00ful))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "extra load/store", data);
 }
 
 static int
 arm_reloc_ldr_str_ldrb_strb (uint32_t insn, struct arm_insn_reloc_data *data,
 			     int load, int size, int usermode)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000ff00ful))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "load/store", data);
 }
 
 static int
 arm_reloc_preload (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000f0000ul))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "preload", data);
 }
 
 static int
 arm_reloc_preload_reg (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  if (arm_insn_references_pc (insn, 0x000f000ful))
+    return arm_reloc_refs_pc_error (data);
+
+  return arm_reloc_others (insn, "preload reg", data);
 }
 
 static int
 arm_reloc_svc (uint32_t insn, struct arm_insn_reloc_data *data)
 {
-  return 1;
+  /* There is nothing PC-relative in the SVC instruction.  */
+  return arm_reloc_others(insn, "svc", data);
 }
 
 static int
 arm_reloc_undef (uint32_t insn, struct arm_insn_reloc_data *data)
 {
+
+  data->err = "The instruction is undefined, couldn't relocate.";
   return 1;
 }
 
 static int
 arm_reloc_unpred (uint32_t insn, struct arm_insn_reloc_data *data)
 {
+  data->err = "The instruction is unpredictable, couldn't relocate.";
   return 1;
 }
 
@@ -1186,6 +1281,8 @@ copy_instruction_arm (CORE_ADDR *to, CORE_ADDR from, const char **err)
   /* Set a default generic error message, which can be overridden by the
      relocation functions.  */
   data.err = "Error relocating instruction.";
+  data.orig_loc = from;
+  data.new_loc = *to;
 
   ret = arm_relocate_insn (insn, &arm_insn_reloc_visitor, &data);
   if (ret != 0)
diff --git a/gdb/testsuite/gdb.trace/ftrace-arm-insn.S b/gdb/testsuite/gdb.trace/ftrace-arm-insn.S
new file mode 100644
index 0000000..c123727
--- /dev/null
+++ b/gdb/testsuite/gdb.trace/ftrace-arm-insn.S
@@ -0,0 +1,221 @@
+.syntax unified
+
+.text
+
+.macro prologue
+push {lr}
+.endm
+
+.macro epilogue
+pop {pc}
+.endm
+
+/* arm b imm */
+.arm
+.type func_arm_b_imm, STT_FUNC
+.global func_arm_b_imm
+func_arm_b_imm:
+	prologue
+
+insn_arm_b_imm:
+	b arm_b_imm_jump
+
+arm_b_imm_jump_back:
+	epilogue
+
+arm_b_imm_jump:
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+
+	# return to the function
+	b arm_b_imm_jump_back
+
+
+/* arm b imm with a false condition */
+.arm
+.type func_arm_b_imm_cond, STT_FUNC
+.global func_arm_b_imm_cond
+func_arm_b_imm_cond:
+	prologue
+
+	/* Force a false condition.  If we mess up the condition and the branch is
+	   taken, the magic number won't get written and the test will fail.  */
+	mov r0, 0
+	cmp r0, 0
+
+insn_arm_b_imm_cond:
+	bne arm_b_imm_jump_cond
+
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+
+arm_b_imm_jump_cond:
+
+	epilogue
+
+
+/* arm bl imm */
+.arm
+.type func_arm_bl_imm, STT_FUNC
+.global func_arm_bl_imm
+func_arm_bl_imm:
+	prologue
+
+insn_arm_bl_imm:
+	bl arm_bl_imm_jump
+
+	epilogue
+
+arm_bl_imm_jump:
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+
+	# return to the function
+	bx lr
+
+
+/* arm blx imm */
+.arm
+.type func_arm_blx_imm, STT_FUNC
+.global func_arm_blx_imm
+func_arm_blx_imm:
+	prologue
+
+insn_arm_blx_imm:
+	blx arm_blx_imm_jump
+
+	epilogue
+
+.thumb
+arm_blx_imm_jump:
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+
+	# return to the function
+	bx lr
+
+
+/* arm bx reg */
+.arm
+.type func_arm_bx_reg, STT_FUNC
+.global func_arm_bx_reg
+func_arm_bx_reg:
+	prologue
+
+	ldr r0, =arm_bx_reg_jump
+
+insn_arm_bx_reg:
+	bx r0
+
+arm_bx_reg_jump_back:
+	epilogue
+
+.thumb_func
+.thumb
+arm_bx_reg_jump:
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+
+	# return to the function
+	ldr r0, =arm_bx_reg_jump_back
+	bx r0
+
+
+/* arm blx reg */
+.arm
+.type func_arm_blx_reg, STT_FUNC
+.global func_arm_blx_reg
+func_arm_blx_reg:
+	prologue
+
+	ldr r0, =arm_blx_reg_jump
+
+insn_arm_blx_reg:
+	blx r0
+
+	epilogue
+
+.thumb_func
+.thumb
+arm_blx_reg_jump:
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+
+	# return to the function
+	bx lr
+
+
+/* arm ldm */
+.arm
+.type func_arm_ldm, STT_FUNC
+.global func_arm_ldm
+func_arm_ldm:
+	prologue
+
+	ldr r0, =magic_number
+	ldr r0, [r0]
+	stmfd sp!, {r0}
+
+insn_arm_ldm:
+	ldmfd sp!, {r1}
+	ldr r0, =global_variable
+	str r1, [r0]
+
+	epilogue
+
+
+/* arm ldm pc */
+.arm
+.type func_arm_ldm_pc, STT_FUNC
+.global func_arm_ldm_pc
+func_arm_ldm_pc:
+	prologue
+
+	ldr r0, =arm_ldm_pc_jump
+	stmfd sp!, {r0}
+
+insn_arm_ldm_pc:
+	ldmfd sp!, {pc}
+
+arm_ldm_pc_jump_back:
+	epilogue
+
+arm_ldm_pc_jump:
+	ldr r0, =global_variable
+	ldr r1, =magic_number
+	ldr r1, [r1]
+	str r1, [r0]
+	b arm_ldm_pc_jump_back
+
+
+/* arm stm */
+.arm
+.type func_arm_stm, STT_FUNC
+.global func_arm_stm
+func_arm_stm:
+	prologue
+
+	ldr r0, =magic_number
+	ldr r0, [r0]
+
+insn_arm_stm:
+	stmfd sp!, {r0}
+
+	ldmfd sp!, {r1}
+	ldr r0, =global_variable
+	str r1, [r0]
+
+	epilogue
+
diff --git a/gdb/testsuite/gdb.trace/ftrace-arm-insn.c b/gdb/testsuite/gdb.trace/ftrace-arm-insn.c
new file mode 100644
index 0000000..ec08298
--- /dev/null
+++ b/gdb/testsuite/gdb.trace/ftrace-arm-insn.c
@@ -0,0 +1,78 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+/* Magic number chosen at random.  */
+const uint32_t magic_number = 0x2eb2944b;
+
+static void
+break_here (void)
+{
+}
+
+void
+fail (void)
+{
+  exit (1);
+}
+
+int global_variable;
+
+#define DEF_TEST_FN(name) \
+   static void \
+   test_##name (void) \
+   { \
+      void func_##name (void); \
+      \
+      global_variable = 0; \
+      \
+      func_##name (); \
+      \
+      if (global_variable != magic_number) \
+         fail (); \
+      \
+      break_here (); \
+   }
+
+DEF_TEST_FN (arm_b_imm)
+DEF_TEST_FN (arm_b_imm_cond)
+DEF_TEST_FN (arm_bl_imm)
+DEF_TEST_FN (arm_blx_imm)
+DEF_TEST_FN (arm_bx_reg)
+DEF_TEST_FN (arm_blx_reg)
+DEF_TEST_FN (arm_ldm)
+DEF_TEST_FN (arm_ldm_pc)
+DEF_TEST_FN (arm_stm)
+
+int
+main (void)
+{
+  test_arm_b_imm ();
+  test_arm_b_imm_cond ();
+  test_arm_bl_imm ();
+  test_arm_blx_imm ();
+  test_arm_bx_reg ();
+  test_arm_blx_reg ();
+  test_arm_ldm ();
+  test_arm_ldm_pc ();
+  test_arm_stm ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.trace/ftrace-arm-insn.exp b/gdb/testsuite/gdb.trace/ftrace-arm-insn.exp
new file mode 100644
index 0000000..e78d484
--- /dev/null
+++ b/gdb/testsuite/gdb.trace/ftrace-arm-insn.exp
@@ -0,0 +1,103 @@
+# Copyright 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib "trace-support.exp"
+
+standard_testfile
+set expfile $testfile.exp
+
+# Some targets have leading underscores on assembly symbols.
+set additional_flags [gdb_target_symbol_prefix_flags]
+
+set c_file "$srcdir/$subdir/$srcfile"
+set asm_file "$srcdir/$subdir/$testfile.S"
+set source_files "${c_file} ${asm_file}"
+
+if { ![istarget "arm*-*-*"] } {
+    verbose "Skipping ARM-specific test."
+    return
+}
+
+if [prepare_for_testing $expfile $binfile $source_files \
+	[list debug $additional_flags]] {
+    untested "failed to prepare for trace tests"
+    return -1
+}
+
+if ![runto_main] {
+    fail "Can't run to main to check for trace support"
+    return -1
+}
+
+if ![gdb_target_supports_trace] {
+    unsupported "target does not support trace"
+    return -1
+}
+
+if {![gdb_target_supports_fast_trace]} {
+    unsupported "Target does not support fast tracepoints."
+    return -1
+}
+
+set libipa [get_in_proc_agent]
+gdb_load_shlib $libipa
+
+if ![runto_main] {
+    untested "could not run to main"
+    return -1
+}
+
+if { [gdb_compile $source_files $binfile \
+	  executable [list debug $additional_flags shlib=$libipa] ] != "" } {
+    untested "failed to compile ftrace tests"
+    return -1
+}
+
+proc do_test { insn } {
+    global binfile
+    global decimal
+    global hex
+    global asm_file
+
+    with_test_prefix $insn {
+        # The test function for this instruction
+        set test_function "test_${insn}"
+        # The instruction on which we want to install a fast tracepoint
+        set insn_location "insn_${insn}"
+
+        clean_restart ${binfile}
+        if ![runto $test_function] {
+            fail "Can't run to $test_function"
+            return 0
+        }
+        gdb_breakpoint "break_here"
+
+        gdb_test "ftrace ${insn_location}" "Fast tracepoint ${decimal} at ${hex}: file ${asm_file}, line ${decimal}."
+        gdb_test_no_output "tstart"
+        gdb_continue "break_here"
+        gdb_test_no_output "tstop"
+        gdb_test "tstatus" ".*Collected 1 trace frames.*"
+    }
+}
+
+do_test arm_b_imm
+do_test arm_b_imm_cond
+do_test arm_bl_imm
+do_test arm_blx_imm
+do_test arm_bx_reg
+do_test arm_blx_reg
+do_test arm_ldm
+do_test arm_ldm_pc
+do_test arm_stm
