* 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:
parent
177321bd85
commit
f9d67f435f
|
@ -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)
|
||||
|
|
250
gdb/arm-tdep.c
250
gdb/arm-tdep.c
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue