@@ -3,6 +3,10 @@
*** Changes since GDB 7.11
+* Support for fast tracepoints on arm-linux was added in GDBserver,
+ including JIT compiling fast tracepoint's conditional expression
+ bytecode into native code.
+
* Support for tracepoints on arm-linux was added in GDBServer.
* Fortran: Support structures with fields of dynamic types and
@@ -1310,35 +1310,167 @@ static int
thumb32_reloc_alu_imm (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
+ uint16_t rm = bits (insn2, 0, 3);
+ uint16_t rd = bits (insn2, 8, 11);
+
+ /* This is essentially about the MOV (register, Thumb) instruction. Allow
+ relocation if it doesn't reference the PC. */
+ if (rm == ARM_PC_REGNUM || rd == ARM_PC_REGNUM)
+ return arm_reloc_refs_pc_error (data);
+
+ return thumb32_reloc_others (insn1, insn2, "ALU imm", data);
}
static int
thumb32_reloc_b_bl_blx (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
+ int ret = 1;
+ long offset;
+ CORE_ADDR absolute_dest;
+ uint16_t link = bit (insn2, 14);
+
+ int j1 = bit (insn2, 13);
+ int j2 = bit (insn2, 11);
+ int i1 = !(j1 ^ bit (insn1, 10));
+ int i2 = !(j2 ^ bit (insn1, 10));
+ int s = sbits (insn1, 10, 10);
+
+ if (!link)
+ {
+ /* It's a b. */
+ int imm11 = bits (insn2, 0, 10);
+
+ if (!bit (insn2, 12))
+ {
+ /* Encoding T3, with a condition and smaller immediate. */
+
+ int imm6 = bits (insn1, 0, 5);
+ int cond = bits (insn1, 6, 9);
+
+ offset = ((imm11 << 1) |
+ (imm6 << 12) |
+ (j1 << 18) |
+ (j2 << 19) |
+ (s << 20)) + 4;
+
+ absolute_dest = data->orig_loc + offset;
+
+ arm_emit_thumb_bw_cond (data->insns.thumb32, cond,
+ arm_thumb_branch_relative_distance
+ (data->new_loc, absolute_dest));
+ }
+ else
+ {
+ /* Encoding T4, with no condition but a larger immediate */
+ int imm10 = bits (insn1, 0, 9);
+
+ offset = ((imm11 << 1) |
+ (imm10 << 12) |
+ (i2 << 22) |
+ (i1 << 23) |
+ (s << 24)) + 4;
+
+ absolute_dest = data->orig_loc + offset;
+
+ arm_emit_thumb_bw
+ (data->insns.thumb32, arm_thumb_branch_relative_distance
+ (data->new_loc, absolute_dest));
+ }
+
+ ret = 0;
+ }
+ else
+ {
+ int exchange = !bit (insn2, 12);
+
+ if (!exchange)
+ {
+ /* It's a bl. */
+ int imm11 = bits (insn2, 0, 10);
+ int imm10 = bits (insn1, 0, 9);
+
+ offset = ((imm11 << 1)
+ | (imm10 << 12)
+ | (i2 << 22)
+ | (i1 << 23)
+ | (s << 24)) + 4;
+
+ absolute_dest = data->orig_loc + offset;
+
+ arm_emit_thumb_bl (data->insns.thumb32,
+ arm_thumb_branch_relative_distance
+ (data->new_loc, absolute_dest));
+ ret = 0;
+ }
+ else
+ {
+ /* It's a blx. */
+ int imm10l = bits (insn2, 1, 10);
+ int imm10h = bits (insn1, 0, 9);
+
+ offset = ((imm10l << 2)
+ | (imm10h << 12)
+ | (i2 << 22)
+ | (i1 << 23)
+ | (s << 24)) + 4;
+
+ absolute_dest = data->orig_loc + offset;
+
+ arm_emit_thumb_blx (data->insns.thumb32,
+ immediate_operand
+ (arm_thumb_to_arm_branch_relative_distance
+ (data->new_loc, absolute_dest)));
+ ret = 0;
+ }
+ }
+
+ return ret;
}
static int
thumb32_reloc_block_xfer (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
+ uint16_t rn = bits (insn1, 0, 3);
+ uint16_t load = bit (insn1, 4);
+
+ /* For LDM/POP instructions, there is not problem executing out of line even
+ if PC is part of the register list. It will simply result in an absolute
+ jump to the address popped.
+
+ For STM/PUSH instructions in Thumb mode, PC can't be in the register list,
+ unlike in ARM mode. So we don't need to check if it's present.
+ */
+
+ return thumb32_reloc_others (insn1, insn2, "ldm/stm", data);
}
static int
thumb32_reloc_copro_load_store (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
+ uint16_t rn = bits (insn1, 0, 3);
+
+ if (rn == ARM_PC_REGNUM)
+ return arm_reloc_refs_pc_error (data);
+
+ return thumb32_reloc_others (insn1, insn2, "copro load/store", data);
}
static int
thumb32_reloc_load_literal (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data, int size)
{
- return 1;
+ /* It should be possible to relocate this. The difficulty is that the
+ 12 offset bits are likely not to be enough to encode the offset at the new
+ location. We might be able to replace it by multiple instructions, loading
+ the absolute offset in a register, and using LDR (immediate, Thumb). We
+ would need to take care of which register we use and save the relevant
+ registers value.
+ */
+
+ return arm_reloc_refs_pc_error (data);
}
static int
@@ -1346,27 +1478,45 @@ thumb32_reloc_load_reg_imm (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data, int writeback,
int immed)
{
- return 1;
+ uint16_t rn = bits (insn1, 0, 3);
+
+ /* If rn is the PC, we should end up in load_literal, and that means the
+ instruction decoder has a bug. */
+ gdb_assert (rn != ARM_PC_REGNUM);
+
+ /* The instruction is safe even if the destination register (rt) is the PC,
+ as it will simply result in a branch. */
+ return thumb32_reloc_others (insn1, insn2, "load reg", data);
}
static int
thumb32_reloc_pc_relative_32bit (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
+ /* Form PC-relative Address. We could re-encode this instruction with the
+ new PC of the instruction, if we determine that this instruction is common
+ enough for it to be worth it. */
+
+ return arm_reloc_refs_pc_error (data);
}
static int
thumb32_reloc_preload (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
+ unsigned int rn = bits (insn1, 0, 3);
+
+ if (rn != ARM_PC_REGNUM)
+ return arm_reloc_refs_pc_error (data);
+
+ return thumb32_reloc_others (insn1, insn2, "preload", data);
}
static int
thumb32_reloc_undef (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
+ data->err = "The instruction is undefined, couldn't relocate.";
return 1;
}
@@ -1374,9 +1524,12 @@ static int
thumb32_reloc_table_branch (uint16_t insn1, uint16_t insn2,
struct arm_insn_reloc_data *data)
{
- return 1;
-}
+ /* The values in the table are relative offsets to PC. We could generate a
+ sequence of instructions to reproduce the same behavior. but these
+ instructions are probably not common enough for it to be worth it. */
+ return arm_reloc_refs_pc_error (data);
+}
struct thumb_32bit_insn_reloc_visitor thumb_32bit_insns_reloc_visitor = {
thumb32_reloc_alu_imm,
@@ -1416,6 +1569,8 @@ copy_instruction_thumb32 (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 = thumb_32bit_relocate_insn (insn1, insn2,
&thumb_32bit_insns_reloc_visitor, &data);
@@ -219,3 +219,160 @@ insn_arm_stm:
epilogue
+
+/* thumb b imm (should test the encoding T4) */
+.thumb
+.type func_thumb_b_imm, STT_FUNC
+.global func_thumb_b_imm
+func_thumb_b_imm:
+ prologue
+
+insn_thumb_b_imm:
+ b.w thumb_b_imm_jump
+
+thumb_b_imm_jump_back:
+ epilogue
+
+thumb_b_imm_jump:
+ ldr r0, =global_variable
+ ldr r1, =magic_number
+ ldr r1, [r1]
+ str r1, [r0]
+
+ # return to the function
+ b thumb_b_imm_jump_back
+
+
+/* thumb b imm cond (should test the encoding T3) */
+.thumb
+.type func_thumb_b_imm_cond, STT_FUNC
+.global func_thumb_b_imm_cond
+func_thumb_b_imm_cond:
+ prologue
+
+ /* Make a comparison that evaluates to true */
+ cmp r0, r0
+
+insn_thumb_b_imm_cond:
+ bne.w thumb_b_imm_cond_jump
+
+ ldr r0, =global_variable
+ ldr r1, =magic_number
+ ldr r1, [r1]
+ str r1, [r0]
+
+thumb_b_imm_cond_jump:
+ epilogue
+
+/* thumb bl imm */
+.thumb
+.type func_thumb_bl_imm, STT_FUNC
+.global func_thumb_bl_imm
+func_thumb_bl_imm:
+ prologue
+
+insn_thumb_bl_imm:
+ bl.w thumb_bl_imm_jump
+
+ epilogue
+
+thumb_bl_imm_jump:
+ ldr r0, =global_variable
+ ldr r1, =magic_number
+ ldr r1, [r1]
+ str r1, [r0]
+
+ # return to the function
+ bx lr
+
+
+/* thumb blx imm */
+.thumb
+.type func_thumb_blx_imm, STT_FUNC
+.global func_thumb_blx_imm
+func_thumb_blx_imm:
+ prologue
+
+insn_thumb_blx_imm:
+ blx.w thumb_blx_imm_jump
+
+ epilogue
+
+.arm
+thumb_blx_imm_jump:
+ ldr r0, =global_variable
+ ldr r1, =magic_number
+ ldr r1, [r1]
+ str r1, [r0]
+
+ # return to the function
+ bx lr
+
+
+/* thumb ldm */
+.thumb
+.type func_thumb_ldm, STT_FUNC
+.global func_thumb_ldm
+func_thumb_ldm:
+ prologue
+
+ /* Store magic number in r0. */
+ ldr r0, =magic_number
+ ldr r0, [r0]
+
+ /* Push the magic number on the stack. We need to use at least two
+ registers for the stm/ldm, otherwise the ldm will be encoded as an
+ ldr. */
+ stmfd sp!, {r0, r1}
+ mov r0, 0
+
+insn_thumb_ldm:
+ ldmfd.w sp!, {r0, r1}
+ ldr r1, =global_variable
+ str r0, [r1]
+
+ epilogue
+
+
+/* thumb ldm pc */
+.thumb
+.type func_thumb_ldm_pc, STT_FUNC
+.global func_thumb_ldm_pc
+func_thumb_ldm_pc:
+ prologue
+
+ bl thumb_ldm_pc_jump
+
+ ldr r0, =global_variable
+ ldr r1, =magic_number
+ ldr r1, [r1]
+ str r1, [r0]
+
+ epilogue
+
+thumb_ldm_pc_jump:
+ /* We need to specify at least two registers, otherwise the ldm instruction
+ will get encoded as an ldr. */
+ stmfd sp!, {r0, lr}
+insn_thumb_ldm_pc:
+ ldmfd.w sp!, {r0, pc}
+
+
+/* thumb stm */
+.thumb
+.type func_thumb_stm, STT_FUNC
+.global func_thumb_stm
+func_thumb_stm:
+ prologue
+
+ ldr r0, =magic_number
+ ldr r0, [r0]
+
+insn_thumb_stm:
+ stmfd.w sp!, {r0}
+
+ ldmfd sp!, {r1}
+ ldr r0, =global_variable
+ str r1, [r0]
+
+ epilogue
@@ -61,6 +61,14 @@ DEF_TEST_FN (arm_ldm)
DEF_TEST_FN (arm_ldm_pc)
DEF_TEST_FN (arm_stm)
+DEF_TEST_FN (thumb_b_imm)
+DEF_TEST_FN (thumb_b_imm_cond)
+DEF_TEST_FN (thumb_bl_imm)
+DEF_TEST_FN (thumb_blx_imm)
+DEF_TEST_FN (thumb_ldm)
+DEF_TEST_FN (thumb_ldm_pc)
+DEF_TEST_FN (thumb_stm)
+
int
main (void)
{
@@ -74,5 +82,13 @@ main (void)
test_arm_ldm_pc ();
test_arm_stm ();
+ test_thumb_b_imm ();
+ test_thumb_b_imm_cond ();
+ test_thumb_bl_imm ();
+ test_thumb_blx_imm ();
+ test_thumb_ldm ();
+ test_thumb_ldm_pc ();
+ test_thumb_stm ();
+
return 0;
}
@@ -101,3 +101,11 @@ do_test arm_blx_reg
do_test arm_ldm
do_test arm_ldm_pc
do_test arm_stm
+
+do_test thumb_b_imm
+do_test thumb_b_imm_cond
+do_test thumb_bl_imm
+do_test thumb_blx_imm
+do_test thumb_ldm
+do_test thumb_ldm_pc
+do_test thumb_stm