@@ -1087,11 +1087,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)
{
@@ -1103,86 +1116,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;
}
@@ -1220,6 +1315,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)
new file mode 100644
@@ -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
+
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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