+2011-04-01 Ulrich Weigand <ulrich.weigand@linaro.org>
+
+ * arm-tdep.h (arm_insert_single_step_breakpoint): Add prototype.
+ * arm-tdep.c (arm_override_mode): New global.
+ (arm_pc_is_thumb): Respect arm_override_mode. Remove single-step
+ execution mode heuristics.
+ (thumb_get_next_pc_raw): Remove INSERT_BKTP argument; always insert
+ second single-step breakpoint if needed, using
+ arm_insert_single_step_breakpoint.
+ (arm_get_next_pc_raw): Remove INSERT_BKTP argument. Only handle
+ ARM execution mode, do not call thumb_get_next_pc_raw.
+ (arm_get_next_pc): Encode execution mode in return value. Call
+ either arm_get_next_pc_raw or thumb_get_next_pc_raw.
+ (arm_insert_single_step_breakpoint): New function.
+ (arm_software_single_step): Call it.
+ * arm-linux-tdep.c (arm_linux_sigreturn_return_addr): Add IS_THUMB
+ argument to return execution mode of sigreturn target.
+ (arm_linux_syscall_next_pc): Use it.
+ (arm_linux_copy_svc): Update call.
+ (arm_linux_software_single_step): Call
+ arm_insert_single_step_breakpoint.
+
2011-03-31 Jan Kratochvil <jan.kratochvil@redhat.com>
* dwarf2read.c (dwarf2_read_index): Fix .gdb_index version number in
}
/* Copy the value of next pc of sigreturn and rt_sigrturn into PC,
- and return 1. Return 0 if it is not a rt_sigreturn/sigreturn
- syscall. */
+ return 1. In addition, set IS_THUMB depending on whether we
+ will return to ARM or Thumb code. Return 0 if it is not a
+ rt_sigreturn/sigreturn syscall. */
static int
arm_linux_sigreturn_return_addr (struct frame_info *frame,
unsigned long svc_number,
- CORE_ADDR *pc)
+ CORE_ADDR *pc, int *is_thumb)
{
/* Is this a sigreturn or rt_sigreturn syscall? */
if (svc_number == 119 || svc_number == 173)
{
if (get_frame_type (frame) == SIGTRAMP_FRAME)
{
+ ULONGEST t_bit = arm_psr_thumb_bit (frame_unwind_arch (frame));
+ CORE_ADDR cpsr
+ = frame_unwind_register_unsigned (frame, ARM_PS_REGNUM);
+
+ *is_thumb = (cpsr & t_bit) != 0;
*pc = frame_unwind_caller_pc (frame);
return 1;
}
CORE_ADDR return_addr = 0;
int is_thumb = arm_frame_is_thumb (frame);
ULONGEST svc_number = 0;
- int is_sigreturn = 0;
if (is_thumb)
{
svc_number = get_frame_register_unsigned (frame, 7);
+ return_addr = pc + 2;
}
else
{
{
svc_number = get_frame_register_unsigned (frame, 7);
}
+
+ return_addr = pc + 4;
}
- is_sigreturn = arm_linux_sigreturn_return_addr (frame, svc_number,
- &return_addr);
+ arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
- if (is_sigreturn)
- return return_addr;
-
+ /* Addresses for calling Thumb functions have the bit 0 set. */
if (is_thumb)
- {
- return_addr = pc + 2;
- /* Addresses for calling Thumb functions have the bit 0 set. */
- return_addr |= 1;
- }
- else
- {
- return_addr = pc + 4;
- }
+ return_addr |= 1;
return return_addr;
}
if (next_pc > 0xffff0000)
next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
- insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+ arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
return 1;
}
struct frame_info *frame;
unsigned int svc_number = displaced_read_reg (regs, dsc, 7);
int is_sigreturn = 0;
+ int is_thumb;
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying Linux svc insn %.8lx\n",
frame = get_current_frame ();
is_sigreturn = arm_linux_sigreturn_return_addr(frame, svc_number,
- &return_to);
+ &return_to, &is_thumb);
if (is_sigreturn)
{
struct symtab_and_line sal;
static const char *arm_fallback_mode_string = "auto";
static const char *arm_force_mode_string = "auto";
+/* Internal override of the execution mode. -1 means no override,
+ 0 means override to ARM mode, 1 means override to Thumb mode.
+ The effect is the same as if arm_force_mode has been set by the
+ user (except the internal override has precedence over a user's
+ arm_force_mode override). */
+static int arm_override_mode = -1;
+
/* Number of different reg name sets (options). */
static int num_disassembly_options;
return 0;
}
-static CORE_ADDR arm_get_next_pc_raw (struct frame_info *frame,
- CORE_ADDR pc, int insert_bkpt);
-
/* Determine if the program counter specified in MEMADDR is in a Thumb
function. This function should be called for addresses unrelated to
any executing frame; otherwise, prefer arm_frame_is_thumb. */
if (IS_THUMB_ADDR (memaddr))
return 1;
+ /* Respect internal mode override if active. */
+ if (arm_override_mode != -1)
+ return arm_override_mode;
+
/* If the user wants to override the symbol table, let him. */
if (strcmp (arm_force_mode_string, "arm") == 0)
return 0;
target, then trust the current value of $cpsr. This lets
"display/i $pc" always show the correct mode (though if there is
a symbol table we will not reach here, so it still may not be
- displayed in the mode it will be executed).
-
- As a further heuristic if we detect that we are doing a single-step we
- see what state executing the current instruction ends up with us being
- in. */
+ displayed in the mode it will be executed). */
if (target_has_registers)
- {
- struct frame_info *current_frame = get_current_frame ();
- CORE_ADDR current_pc = get_frame_pc (current_frame);
- int is_thumb = arm_frame_is_thumb (current_frame);
- CORE_ADDR next_pc;
- if (memaddr == current_pc)
- return is_thumb;
- else
- {
- struct gdbarch *gdbarch = get_frame_arch (current_frame);
- next_pc = arm_get_next_pc_raw (current_frame, current_pc, FALSE);
- if (memaddr == gdbarch_addr_bits_remove (gdbarch, next_pc))
- return IS_THUMB_ADDR (next_pc);
- else
- return is_thumb;
- }
- }
+ return arm_frame_is_thumb (get_current_frame ());
/* Otherwise we're out of luck; we assume ARM. */
return 0;
another breakpoint by our caller. */
static CORE_ADDR
-thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
+thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
struct address_space *aspace = get_frame_address_space (frame);
/* Set a breakpoint on the following instruction. */
gdb_assert ((itstate & 0x0f) != 0);
- if (insert_bkpt)
- insert_single_step_breakpoint (gdbarch, aspace, pc);
+ arm_insert_single_step_breakpoint (gdbarch, aspace,
+ MAKE_THUMB_ADDR (pc));
cond_negated = (itstate >> 4) & 1;
/* Skip all following instructions with the same
}
/* Get the raw next address. PC is the current program counter, in
- FRAME. INSERT_BKPT should be TRUE if we want a breakpoint set on
- the alternative next instruction if there are two options.
+ FRAME, which is assumed to be executing in ARM mode.
The value returned has the execution state of the next instruction
encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is
address. */
static CORE_ADDR
-arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
+arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
unsigned long status;
CORE_ADDR nextpc;
- if (arm_frame_is_thumb (frame))
- return thumb_get_next_pc_raw (frame, pc, insert_bkpt);
-
pc_val = (unsigned long) pc;
this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
return nextpc;
}
+/* Determine next PC after current instruction executes. Will call either
+ arm_get_next_pc_raw or thumb_get_next_pc_raw. Error out if infinite
+ loop is detected. */
+
CORE_ADDR
arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- CORE_ADDR nextpc =
- gdbarch_addr_bits_remove (gdbarch,
- arm_get_next_pc_raw (frame, pc, TRUE));
- if (nextpc == pc)
- error (_("Infinite loop detected"));
+ CORE_ADDR nextpc;
+
+ if (arm_frame_is_thumb (frame))
+ {
+ nextpc = thumb_get_next_pc_raw (frame, pc);
+ if (nextpc == MAKE_THUMB_ADDR (pc))
+ error (_("Infinite loop detected"));
+ }
+ else
+ {
+ nextpc = arm_get_next_pc_raw (frame, pc);
+ if (nextpc == pc)
+ error (_("Infinite loop detected"));
+ }
+
return nextpc;
}
+/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
+ of the appropriate mode (as encoded in the PC value), even if this
+ differs from what would be expected according to the symbol tables. */
+
+void
+arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
+ struct address_space *aspace,
+ CORE_ADDR pc)
+{
+ struct cleanup *old_chain
+ = make_cleanup_restore_integer (&arm_override_mode);
+
+ arm_override_mode = IS_THUMB_ADDR (pc);
+ pc = gdbarch_addr_bits_remove (gdbarch, pc);
+
+ insert_single_step_breakpoint (gdbarch, aspace, pc);
+
+ do_cleanups (old_chain);
+}
+
/* single_step() is called just before we want to resume the inferior,
if we want to single-step it but there is no hardware or kernel
single-step support. We find the target of the coming instruction
{
struct gdbarch *gdbarch = get_frame_arch (frame);
struct address_space *aspace = get_frame_address_space (frame);
-
- /* NOTE: This may insert the wrong breakpoint instruction when
- single-stepping over a mode-changing instruction, if the
- CPSR heuristics are used. */
-
CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
- insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+
+ arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
return 1;
}
--- /dev/null
+# Copyright 2011 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/>.
+
+# Test single-stepping into incorrectly marked Thumb routine
+
+if {![istarget arm*-*]} then {
+ verbose "Skipping ARM tests."
+ return
+}
+
+set testfile "thumb-singlestep"
+set srcfile ${testfile}.S
+
+set additional_flags "additional_flags=-mthumb"
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} [list debug $additional_flags]] {
+ untested ${testfile}.exp
+ return -1
+}
+
+if ![runto_main] then {
+ untested ${testfile}.exp
+ return -1
+}
+
+gdb_test "si" "foo \\(\\) at .*${srcfile}.*mov r0,#42.*" "step into foo"
+