* 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.

gdb/testsuite/
	* gdb.arch/thumb-singlestep.S: New file.
	* gdb.arch/thumb-singlestep.exp: Likewise.
This commit is contained in:
Ulrich Weigand
2011-04-01 11:57:03 +00:00
parent 592588f3f8
commit 18819fa6ff
7 changed files with 185 additions and 67 deletions

View File

@@ -133,6 +133,13 @@ static const char *arm_mode_strings[] =
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;
@@ -356,9 +363,6 @@ arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start)
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. */
@@ -388,6 +392,10 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
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;
@@ -418,29 +426,9 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
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;
@@ -4216,7 +4204,7 @@ thumb_advance_itstate (unsigned int itstate)
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);
@@ -4314,8 +4302,8 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
/* 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
@@ -4587,8 +4575,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
}
/* 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
@@ -4596,7 +4583,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
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);
@@ -4606,9 +4593,6 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
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);
@@ -4861,18 +4845,51 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
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
@@ -4883,13 +4900,9 @@ arm_software_single_step (struct frame_info *frame)
{
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;
}