* 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.

	testsuite/
	* gdb.arch/thumb2-it.S (it_breakpoints): New function.
	* gdb.arch/thumb2-it.exp (test_it_break): New function.
	(Top level): Call it.
This commit is contained in:
Daniel Jacobowitz 2010-02-01 16:16:30 +00:00
parent 177321bd85
commit f9d67f435f
5 changed files with 308 additions and 18 deletions

View 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)

View 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);

View 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.

View 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__ */

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