gdb/
* 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:
parent
592588f3f8
commit
18819fa6ff
@ -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
|
||||
|
@ -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;
|
||||
|
105
gdb/arm-tdep.c
105
gdb/arm-tdep.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
40
gdb/testsuite/gdb.arch/thumb-singlestep.S
Normal file
40
gdb/testsuite/gdb.arch/thumb-singlestep.S
Normal 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
|
||||
|
38
gdb/testsuite/gdb.arch/thumb-singlestep.exp
Normal file
38
gdb/testsuite/gdb.arch/thumb-singlestep.exp
Normal 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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user