mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-10-13 10:55:46 +08:00
* 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:
@ -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>
|
2010-02-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
|
* 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;
|
return lhs->value < rhs->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine if the program counter specified in MEMADDR is in a Thumb
|
/* Search for the mapping symbol covering MEMADDR. If one is found,
|
||||||
function. This function should be called for addresses unrelated to
|
return its type. Otherwise, return 0. If START is non-NULL,
|
||||||
any executing frame; otherwise, prefer arm_frame_is_thumb. */
|
set *START to the location of the mapping symbol. */
|
||||||
|
|
||||||
static int
|
static char
|
||||||
arm_pc_is_thumb (CORE_ADDR memaddr)
|
arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start)
|
||||||
{
|
{
|
||||||
struct obj_section *sec;
|
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. */
|
/* If there are mapping symbols, consult them. */
|
||||||
sec = find_pc_section (memaddr);
|
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);
|
map_sym = VEC_index (arm_mapping_symbol_s, map, idx);
|
||||||
if (map_sym->value == map_key.value)
|
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)
|
if (idx > 0)
|
||||||
{
|
{
|
||||||
map_sym = VEC_index (arm_mapping_symbol_s, map, idx - 1);
|
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. */
|
/* Thumb functions have a "special" bit set in minimal symbols. */
|
||||||
sym = lookup_minimal_symbol_by_pc (memaddr);
|
sym = lookup_minimal_symbol_by_pc (memaddr);
|
||||||
if (sym)
|
if (sym)
|
||||||
@ -2921,6 +2945,192 @@ arm_software_single_step (struct frame_info *frame)
|
|||||||
return 1;
|
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.
|
/* ARM displaced stepping support.
|
||||||
|
|
||||||
Generally ARM displaced stepping works as follows:
|
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);
|
arm_coff_make_msymbol_special);
|
||||||
set_gdbarch_record_special_symbol (gdbarch, arm_record_special_symbol);
|
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. */
|
/* Virtual tables. */
|
||||||
set_gdbarch_vbit_in_delta (gdbarch, 1);
|
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>
|
2010-02-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.
|
* gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.
|
||||||
|
@ -136,4 +136,47 @@ it_8:
|
|||||||
addlt r0, #8 @ Not reached
|
addlt r0, #8 @ Not reached
|
||||||
bx lr @ Done, Check $r0 == 1
|
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__ */
|
#endif /* __thumb2__ */
|
||||||
|
@ -125,6 +125,17 @@ proc test_it_block { func } {
|
|||||||
return
|
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
|
# If we are using software single-stepping in GDB, then GDB will not
|
||||||
# stop at conditional instructions with a false predicate during stepi.
|
# stop at conditional instructions with a false predicate during stepi.
|
||||||
# If we are using a simulator or debug interface with hardware single
|
# 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 } {
|
for { set i 1 } { $i <= 8 } { incr i } {
|
||||||
test_it_block it_${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}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user