From 18819fa6ffa0c1b99a7e378b625e4aca04707bfe Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Fri, 1 Apr 2011 11:57:03 +0000 Subject: [PATCH] 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. --- gdb/ChangeLog | 22 ++++++ gdb/arm-linux-tdep.c | 38 +++++----- gdb/arm-tdep.c | 105 ++++++++++++++++------------ gdb/arm-tdep.h | 2 + gdb/testsuite/ChangeLog | 5 ++ gdb/testsuite/gdb.arch/thumb-singlestep.S | 40 +++++++++++ gdb/testsuite/gdb.arch/thumb-singlestep.exp | 38 ++++++++++ 7 files changed, 184 insertions(+), 66 deletions(-) create mode 100644 gdb/testsuite/gdb.arch/thumb-singlestep.S create mode 100644 gdb/testsuite/gdb.arch/thumb-singlestep.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ff9e7ba..d40654c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,25 @@ +2011-04-01 Ulrich Weigand + + * 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 * dwarf2read.c (dwarf2_read_index): Fix .gdb_index version number in diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index 2f3109c..9db125e 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -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,24 +727,15 @@ arm_linux_syscall_next_pc (struct frame_info *frame) { 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; } @@ -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; diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 5a5152c..9d8f5ba 100644 --- a/gdb/arm-tdep.c +++ b/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; } diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h index ebd5e6e..356df73 100644 --- a/gdb/arm-tdep.h +++ b/gdb/arm-tdep.h @@ -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); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 58f2a21..ab12f22 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2011-04-01 Ulrich Weigand + + * gdb.arch/thumb-singlestep.S: New file. + * gdb.arch/thumb-singlestep.exp: Likewise. + 2011-03-31 Tom Tromey * gdb.python/py-prettyprint.py (exception_flag): New global. diff --git a/gdb/testsuite/gdb.arch/thumb-singlestep.S b/gdb/testsuite/gdb.arch/thumb-singlestep.S new file mode 100644 index 0000000..63884ca --- /dev/null +++ b/gdb/testsuite/gdb.arch/thumb-singlestep.S @@ -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 . */ + + .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 + diff --git a/gdb/testsuite/gdb.arch/thumb-singlestep.exp b/gdb/testsuite/gdb.arch/thumb-singlestep.exp new file mode 100644 index 0000000..da47c36 --- /dev/null +++ b/gdb/testsuite/gdb.arch/thumb-singlestep.exp @@ -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 . + +# 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" + -- 2.7.4