* 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

@ -1,3 +1,25 @@
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

View File

@ -669,18 +669,24 @@ arm_linux_regset_from_core_section (struct gdbarch *gdbarch,
}
/* 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;
}
@ -698,11 +704,11 @@ arm_linux_syscall_next_pc (struct frame_info *frame)
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
{
@ -721,25 +727,16 @@ arm_linux_syscall_next_pc (struct frame_info *frame)
{
svc_number = get_frame_register_unsigned (frame, 7);
}
}
is_sigreturn = arm_linux_sigreturn_return_addr (frame, svc_number,
&return_addr);
if (is_sigreturn)
return return_addr;
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;
}
arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
/* Addresses for calling Thumb functions have the bit 0 set. */
if (is_thumb)
return_addr |= 1;
return return_addr;
}
@ -761,7 +758,7 @@ arm_linux_software_single_step (struct frame_info *frame)
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;
}
@ -806,6 +803,7 @@ arm_linux_copy_svc (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
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",
@ -814,7 +812,7 @@ arm_linux_copy_svc (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
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;

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;
}

View File

@ -311,6 +311,8 @@ extern void
CORE_ADDR arm_skip_stub (struct frame_info *, CORE_ADDR);
CORE_ADDR arm_get_next_pc (struct frame_info *, CORE_ADDR);
void arm_insert_single_step_breakpoint (struct gdbarch *,
struct address_space *, CORE_ADDR);
int arm_software_single_step (struct frame_info *);
int arm_frame_is_thumb (struct frame_info *frame);

View File

@ -1,3 +1,8 @@
2011-04-01 Ulrich Weigand <ulrich.weigand@linaro.org>
* gdb.arch/thumb-singlestep.S: New file.
* gdb.arch/thumb-singlestep.exp: Likewise.
2011-03-31 Tom Tromey <tromey@redhat.com>
* gdb.python/py-prettyprint.py (exception_flag): New global.

View File

@ -0,0 +1,40 @@
/* Test program with deliberately incorrect execution mode transition
Copyright 2011 Free Software Foundation, Inc.
This file is part of GDB.
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/>. */
.text
.align 2
.global foo
.thumb
/* .thumb_func deliberately omitted */
foo:
mov r0,#42
bx lr
.text
.align 2
.global main
.thumb
.thumb_func
.type main, %function
main:
push {r3, lr}
blx foo
pop {r3, pc}
.size main, .-main

View File

@ -0,0 +1,38 @@
# 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"