* arm-tdep.c (arm_find_mapping_symbol): New function, from
authorDaniel Jacobowitz <drow@false.org>
Mon, 1 Feb 2010 16:16:30 +0000 (16:16 +0000)
committerDaniel Jacobowitz <drow@false.org>
Mon, 1 Feb 2010 16:16:30 +0000 (16:16 +0000)
arm_pc_is_thumb.
(arm_pc_is_thumb): Use arm_find_mapping_symbol.
(extend_buffer_earlier): New function.
(MAX_IT_BLOCK_PREFIX, IT_SCAN_THRESHOLD): New constants.
(arm_adjust_breakpoint_address): New function.
(arm_gdbarch_init): Register arm_adjust_breakpoint_address.

testsuite/
* gdb.arch/thumb2-it.S (it_breakpoints): New function.
* gdb.arch/thumb2-it.exp (test_it_break): New function.
(Top level): Call it.

gdb/ChangeLog
gdb/arm-tdep.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/thumb2-it.S
gdb/testsuite/gdb.arch/thumb2-it.exp

index 72866ca56a023ae5d65c2c6f3a3425aac8557952..b6ce4ad7a260a4a75bdc115ea2fa7a19ce7ab197 100644 (file)
@@ -1,3 +1,13 @@
+2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * arm-tdep.c (arm_find_mapping_symbol): New function, from
+       arm_pc_is_thumb.
+       (arm_pc_is_thumb): Use arm_find_mapping_symbol.
+       (extend_buffer_earlier): New function.
+       (MAX_IT_BLOCK_PREFIX, IT_SCAN_THRESHOLD): New constants.
+       (arm_adjust_breakpoint_address): New function.
+       (arm_gdbarch_init): Register arm_adjust_breakpoint_address.
+
 2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
index e74008354dd40142b08f92b00a92d1a231f20ba8..524e4ace2c0a43902913a039dbb714c2a392bcf6 100644 (file)
@@ -275,25 +275,14 @@ arm_compare_mapping_symbols (const struct arm_mapping_symbol *lhs,
   return lhs->value < rhs->value;
 }
 
-/* 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.  */
+/* Search for the mapping symbol covering MEMADDR.  If one is found,
+   return its type.  Otherwise, return 0.  If START is non-NULL,
+   set *START to the location of the mapping symbol.  */
 
-static int
-arm_pc_is_thumb (CORE_ADDR memaddr)
+static char
+arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start)
 {
   struct obj_section *sec;
-  struct minimal_symbol *sym;
-
-  /* If bit 0 of the address is set, assume this is a Thumb address.  */
-  if (IS_THUMB_ADDR (memaddr))
-    return 1;
-
-  /* If the user wants to override the symbol table, let him.  */
-  if (strcmp (arm_force_mode_string, "arm") == 0)
-    return 0;
-  if (strcmp (arm_force_mode_string, "thumb") == 0)
-    return 1;
 
   /* If there are mapping symbols, consult them.  */
   sec = find_pc_section (memaddr);
@@ -324,18 +313,53 @@ arm_pc_is_thumb (CORE_ADDR memaddr)
                {
                  map_sym = VEC_index (arm_mapping_symbol_s, map, idx);
                  if (map_sym->value == map_key.value)
-                   return map_sym->type == 't';
+                   {
+                     if (start)
+                       *start = map_sym->value + obj_section_addr (sec);
+                     return map_sym->type;
+                   }
                }
 
              if (idx > 0)
                {
                  map_sym = VEC_index (arm_mapping_symbol_s, map, idx - 1);
-                 return map_sym->type == 't';
+                 if (start)
+                   *start = map_sym->value + obj_section_addr (sec);
+                 return map_sym->type;
                }
            }
        }
     }
 
+  return 0;
+}
+
+/* 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.  */
+
+static int
+arm_pc_is_thumb (CORE_ADDR memaddr)
+{
+  struct obj_section *sec;
+  struct minimal_symbol *sym;
+  char type;
+
+  /* If bit 0 of the address is set, assume this is a Thumb address.  */
+  if (IS_THUMB_ADDR (memaddr))
+    return 1;
+
+  /* If the user wants to override the symbol table, let him.  */
+  if (strcmp (arm_force_mode_string, "arm") == 0)
+    return 0;
+  if (strcmp (arm_force_mode_string, "thumb") == 0)
+    return 1;
+
+  /* If there are mapping symbols, consult them.  */
+  type = arm_find_mapping_symbol (memaddr, NULL);
+  if (type)
+    return type == 't';
+
   /* Thumb functions have a "special" bit set in minimal symbols.  */
   sym = lookup_minimal_symbol_by_pc (memaddr);
   if (sym)
@@ -2921,6 +2945,192 @@ arm_software_single_step (struct frame_info *frame)
   return 1;
 }
 
+/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
+   the buffer to be NEW_LEN bytes ending at ENDADDR.  Return
+   NULL if an error occurs.  BUF is freed.  */
+
+static gdb_byte *
+extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
+                      int old_len, int new_len)
+{
+  gdb_byte *new_buf, *middle;
+  int bytes_to_read = new_len - old_len;
+
+  new_buf = xmalloc (new_len);
+  memcpy (new_buf + bytes_to_read, buf, old_len);
+  xfree (buf);
+  if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
+    {
+      xfree (new_buf);
+      return NULL;
+    }
+  return new_buf;
+}
+
+/* An IT block is at most the 2-byte IT instruction followed by
+   four 4-byte instructions.  The furthest back we must search to
+   find an IT block that affects the current instruction is thus
+   2 + 3 * 4 == 14 bytes.  */
+#define MAX_IT_BLOCK_PREFIX 14
+
+/* Use a quick scan if there are more than this many bytes of
+   code.  */
+#define IT_SCAN_THRESHOLD 32
+
+/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
+   A breakpoint in an IT block may not be hit, depending on the
+   condition flags.  */
+static CORE_ADDR
+arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
+{
+  gdb_byte *buf;
+  char map_type;
+  CORE_ADDR boundary, func_start;
+  int buf_len, buf2_len;
+  enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
+  int i, any, last_it, last_it_count;
+
+  /* If we are using BKPT breakpoints, none of this is necessary.  */
+  if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+    return bpaddr;
+
+  /* ARM mode does not have this problem.  */
+  if (!arm_pc_is_thumb (bpaddr))
+    return bpaddr;
+
+  /* We are setting a breakpoint in Thumb code that could potentially
+     contain an IT block.  The first step is to find how much Thumb
+     code there is; we do not need to read outside of known Thumb
+     sequences.  */
+  map_type = arm_find_mapping_symbol (bpaddr, &boundary);
+  if (map_type == 0)
+    /* Thumb-2 code must have mapping symbols to have a chance.  */
+    return bpaddr;
+
+  bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
+
+  if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+      && func_start > boundary)
+    boundary = func_start;
+
+  /* Search for a candidate IT instruction.  We have to do some fancy
+     footwork to distinguish a real IT instruction from the second
+     half of a 32-bit instruction, but there is no need for that if
+     there's no candidate.  */
+  buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
+  if (buf_len == 0)
+    /* No room for an IT instruction.  */
+    return bpaddr;
+
+  buf = xmalloc (buf_len);
+  if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
+    return bpaddr;
+  any = 0;
+  for (i = 0; i < buf_len; i += 2)
+    {
+      unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+      if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+       {
+         any = 1;
+         break;
+       }
+    }
+  if (any == 0)
+    {
+      xfree (buf);
+      return bpaddr;
+    }
+
+  /* OK, the code bytes before this instruction contain at least one
+     halfword which resembles an IT instruction.  We know that it's
+     Thumb code, but there are still two possibilities.  Either the
+     halfword really is an IT instruction, or it is the second half of
+     a 32-bit Thumb instruction.  The only way we can tell is to
+     scan forwards from a known instruction boundary.  */
+  if (bpaddr - boundary > IT_SCAN_THRESHOLD)
+    {
+      int definite;
+
+      /* There's a lot of code before this instruction.  Start with an
+        optimistic search; it's easy to recognize halfwords that can
+        not be the start of a 32-bit instruction, and use that to
+        lock on to the instruction boundaries.  */
+      buf = extend_buffer_earlier (buf, bpaddr, buf_len, IT_SCAN_THRESHOLD);
+      if (buf == NULL)
+       return bpaddr;
+      buf_len = IT_SCAN_THRESHOLD;
+
+      definite = 0;
+      for (i = 0; i < buf_len - sizeof (buf) && ! definite; i += 2)
+       {
+         unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+         if (thumb_insn_size (inst1) == 2)
+           {
+             definite = 1;
+             break;
+           }
+       }
+
+      /* At this point, if DEFINITE, BUF[I] is the first place we
+        are sure that we know the instruction boundaries, and it is far
+        enough from BPADDR that we could not miss an IT instruction
+        affecting BPADDR.  If ! DEFINITE, give up - start from a
+        known boundary.  */
+      if (! definite)
+       {
+         buf = extend_buffer_earlier (buf, bpaddr, buf_len, bpaddr - boundary);
+         if (buf == NULL)
+           return bpaddr;
+         buf_len = bpaddr - boundary;
+         i = 0;
+       }
+    }
+  else
+    {
+      buf = extend_buffer_earlier (buf, bpaddr, buf_len, bpaddr - boundary);
+      if (buf == NULL)
+       return bpaddr;
+      buf_len = bpaddr - boundary;
+      i = 0;
+    }
+
+  /* Scan forwards.  Find the last IT instruction before BPADDR.  */
+  last_it = -1;
+  last_it_count = 0;
+  while (i < buf_len)
+    {
+      unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+      last_it_count--;
+      if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+       {
+         last_it = i;
+         if (inst1 & 0x0001)
+           last_it_count = 4;
+         else if (inst1 & 0x0002)
+           last_it_count = 3;
+         else if (inst1 & 0x0004)
+           last_it_count = 2;
+         else
+           last_it_count = 1;
+       }
+      i += thumb_insn_size (inst1);
+    }
+
+  xfree (buf);
+
+  if (last_it == -1)
+    /* There wasn't really an IT instruction after all.  */
+    return bpaddr;
+
+  if (last_it_count < 1)
+    /* It was too far away.  */
+    return bpaddr;
+
+  /* This really is a trouble spot.  Move the breakpoint to the IT
+     instruction.  */
+  return bpaddr - buf_len + last_it;
+}
+
 /* ARM displaced stepping support.
 
    Generally ARM displaced stepping works as follows:
@@ -6274,6 +6484,10 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
                                         arm_coff_make_msymbol_special);
   set_gdbarch_record_special_symbol (gdbarch, arm_record_special_symbol);
 
+  /* Thumb-2 IT block support.  */
+  set_gdbarch_adjust_breakpoint_address (gdbarch,
+                                        arm_adjust_breakpoint_address);
+
   /* Virtual tables.  */
   set_gdbarch_vbit_in_delta (gdbarch, 1);
 
index 05ecd88d9922f6c01e7ccfe691b8c0271065646c..00f4aeca249c95ff871b907c8e5817def5d08064 100644 (file)
@@ -1,3 +1,9 @@
+2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * gdb.arch/thumb2-it.S (it_breakpoints): New function.
+       * gdb.arch/thumb2-it.exp (test_it_break): New function.
+       (Top level): Call it.
+
 2010-02-01  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.
index a5aab8cb1be5c96f1479b040ac10373bcf13c84e..700c92ad73d2a54186b7e595de2c8f05f6634ecd 100644 (file)
@@ -136,4 +136,47 @@ it_8:
        addlt   r0, #8  @ Not reached
        bx      lr      @ Done, Check $r0 == 1
 
+       .type it_breakpoints,%function
+       .thumb_func
+it_breakpoints:
+       mov     r0, #0
+       cmp     r0, #0
+       it      eq      @ Location 1 @ Break 1
+       moveq   r0, #0
+
+       it      eq      @ Location 2
+       moveq   r0, #0  @ Break 2
+
+       it      ne      @ Location 3
+       movne   r0, #0  @ Break 3
+
+       @ An IT block of maximum size.
+       itttt   eq      @ Location 4
+       moveq.w r0, #0
+       moveq.w r0, #0
+       moveq.w r0, #0
+       moveq.w r0, #0  @ Break 4
+
+       @ Just outside an IT block.
+       it      eq
+       moveq   r0, #0
+       mov     r0, #0  @ Location 5 @ Break 5
+
+       @ After something that looks like an IT block, but
+       @ is the second half of an instruction.
+       .p2align 6
+       cmp     r0, r0
+       b       1f
+       b.w     .+0xe14 @ 0xf000 0xbf08 -> second half is IT EQ
+1:     mov     r0, #0  @ Location 6 @ Break 6
+
+       @ After something that looks like an IT block, but
+       @ is data.
+       .p2align 6
+       b       1f
+       .short  0xbf08
+1:     mov     r0, #0  @ Location 7 @ Break 7
+
+       bx      lr
+
 #endif /* __thumb2__ */
index 5ef8475fde2ba5735e9afe9709c86896470598b8..5144ac16f2a1086e94fd65a03cd07666bb9f411a 100644 (file)
@@ -125,6 +125,17 @@ proc test_it_block { func } {
     return
 }
 
+proc test_it_break { ndx } {
+    set line [gdb_get_line_number "@ Break ${ndx}"]
+
+    if { ! [gdb_breakpoint "${line}"] } {
+       unresolved "continue to breakpoint: test ${ndx}"
+       return
+    }
+
+    gdb_continue_to_breakpoint "test ${ndx}" ".*@ Location ${ndx}.*"
+}
+
 # If we are using software single-stepping in GDB, then GDB will not
 # stop at conditional instructions with a false predicate during stepi.
 # If we are using a simulator or debug interface with hardware single
@@ -138,3 +149,9 @@ if { [istarget arm*-linux*] } {
 for { set i 1 } { $i <= 8 } { incr i } {
     test_it_block it_${i}
 }
+
+gdb_breakpoint "*it_breakpoints"
+gdb_test "call it_breakpoints()" "Breakpoint.*"
+for { set i 1 } { $i <= 7 } { incr i } {
+    test_it_break ${i}
+}
This page took 0.046727 seconds and 4 git commands to generate.