gdb/
authorUlrich Weigand <uweigand@de.ibm.com>
Fri, 1 Apr 2011 11:57:03 +0000 (11:57 +0000)
committerUlrich Weigand <uweigand@de.ibm.com>
Fri, 1 Apr 2011 11:57:03 +0000 (11:57 +0000)
* 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
gdb/arm-linux-tdep.c
gdb/arm-tdep.c
gdb/arm-tdep.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/thumb-singlestep.S [new file with mode: 0644]
gdb/testsuite/gdb.arch/thumb-singlestep.exp [new file with mode: 0644]

index ff9e7ba43b57c914bd2f9b42e1b51e39999bd476..d40654c804eae9f8f60444e1dc50579ad34bf87f 100644 (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
index 2f3109c15e78ca7c418544a3a4981b49302bbd15..9db125eecfb729c0036cf99fb6402a8c249f1048 100644 (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,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;
index 5a5152cc477af063f510e09d88ef80dd0d77d76c..9d8f5ba86e3a946064a94c42e564b28f8f2d4ce4 100644 (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;
 }
index ebd5e6ee3148d7731646bdf9ef86091e52acde2b..356df73cc2994c133797843b03d7da6f885d66fb 100644 (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);
 
index 58f2a21f9ad077bdc23119c268cb57c9348aa618..ab12f22598732ec61ff3a9e327daa971d0d1ca51 100644 (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.
diff --git a/gdb/testsuite/gdb.arch/thumb-singlestep.S b/gdb/testsuite/gdb.arch/thumb-singlestep.S
new file mode 100644 (file)
index 0000000..63884ca
--- /dev/null
@@ -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
+
diff --git a/gdb/testsuite/gdb.arch/thumb-singlestep.exp b/gdb/testsuite/gdb.arch/thumb-singlestep.exp
new file mode 100644 (file)
index 0000000..da47c36
--- /dev/null
@@ -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"
+