mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-04-06 14:21:43 +08:00
* arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
(arm_linux_thumb2_le_breakpoint): New constants. (arm_linux_init_abi): Set thumb2_breakpoint and thumb2_breakpoint_size. * arm-tdep.c (thumb_insn_size, thumb_advance_itstate): New functions. (thumb_get_next_pc): Add a comment. Rename IT to ITSTATE. Implement support for single stepping through IT blocks if a 32-bit Thumb breakpoint instruction is available. (arm_breakpoint_from_pc): If a 32-bit Thumb breakpoint instruction is available, use it when needed. (arm_remote_breakpoint_from_pc): New function. (arm_gdbarch_init): Register arm_remote_breakpoint_from_pc. * arm-tdep.h (struct gdbarch_tdep): Correct thumb_breakpoint comment. Add thumb2_breakpoint and thumb2_breakpoint_size. gdbserver/ * linux-arm-low.c (thumb_breakpoint_len): Delete. (thumb2_breakpoint): New. (arm_breakpoint_at): Check for Thumb-2 breakpoints. testsuite/ * gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.
This commit is contained in:
parent
a1dcb23a28
commit
177321bd85
@ -1,3 +1,20 @@
|
||||
2010-02-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* arm-linux-tdep.c (arm_linux_thumb2_be_breakpoint)
|
||||
(arm_linux_thumb2_le_breakpoint): New constants.
|
||||
(arm_linux_init_abi): Set thumb2_breakpoint and
|
||||
thumb2_breakpoint_size.
|
||||
* arm-tdep.c (thumb_insn_size, thumb_advance_itstate): New functions.
|
||||
(thumb_get_next_pc): Add a comment. Rename IT to ITSTATE.
|
||||
Implement support for single stepping through IT blocks if
|
||||
a 32-bit Thumb breakpoint instruction is available.
|
||||
(arm_breakpoint_from_pc): If a 32-bit Thumb breakpoint instruction
|
||||
is available, use it when needed.
|
||||
(arm_remote_breakpoint_from_pc): New function.
|
||||
(arm_gdbarch_init): Register arm_remote_breakpoint_from_pc.
|
||||
* arm-tdep.h (struct gdbarch_tdep): Correct thumb_breakpoint
|
||||
comment. Add thumb2_breakpoint and thumb2_breakpoint_size.
|
||||
|
||||
2010-02-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* arch-utils.c (default_remote_breakpoint_from_pc): New function.
|
||||
|
@ -74,6 +74,14 @@ static const char arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
|
||||
|
||||
static const char arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
|
||||
|
||||
/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
|
||||
we must use a length-appropriate breakpoint for 32-bit Thumb
|
||||
instructions. See also thumb_get_next_pc. */
|
||||
|
||||
static const char arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 };
|
||||
|
||||
static const char arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
|
||||
|
||||
/* Description of the longjmp buffer. */
|
||||
#define ARM_LINUX_JB_ELEMENT_SIZE INT_REGISTER_SIZE
|
||||
#define ARM_LINUX_JB_PC 21
|
||||
@ -851,6 +859,7 @@ arm_linux_init_abi (struct gdbarch_info info,
|
||||
else
|
||||
tdep->arm_breakpoint = arm_linux_arm_be_breakpoint;
|
||||
tdep->thumb_breakpoint = arm_linux_thumb_be_breakpoint;
|
||||
tdep->thumb2_breakpoint = arm_linux_thumb2_be_breakpoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -859,9 +868,11 @@ arm_linux_init_abi (struct gdbarch_info info,
|
||||
else
|
||||
tdep->arm_breakpoint = arm_linux_arm_le_breakpoint;
|
||||
tdep->thumb_breakpoint = arm_linux_thumb_le_breakpoint;
|
||||
tdep->thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
|
||||
}
|
||||
tdep->arm_breakpoint_size = sizeof (arm_linux_arm_le_breakpoint);
|
||||
tdep->thumb_breakpoint_size = sizeof (arm_linux_thumb_le_breakpoint);
|
||||
tdep->thumb2_breakpoint_size = sizeof (arm_linux_thumb2_le_breakpoint);
|
||||
|
||||
if (tdep->fp_model == ARM_FLOAT_AUTO)
|
||||
tdep->fp_model = ARM_FLOAT_FPA;
|
||||
|
170
gdb/arm-tdep.c
170
gdb/arm-tdep.c
@ -2256,17 +2256,50 @@ bitcount (unsigned long val)
|
||||
return nbits;
|
||||
}
|
||||
|
||||
/* Return the size in bytes of the complete Thumb instruction whose
|
||||
first halfword is INST1. */
|
||||
|
||||
static int
|
||||
thumb_insn_size (unsigned short inst1)
|
||||
{
|
||||
if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
|
||||
return 4;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int
|
||||
thumb_advance_itstate (unsigned int itstate)
|
||||
{
|
||||
/* Preserve IT[7:5], the first three bits of the condition. Shift
|
||||
the upcoming condition flags left by one bit. */
|
||||
itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
|
||||
|
||||
/* If we have finished the IT block, clear the state. */
|
||||
if ((itstate & 0x0f) == 0)
|
||||
itstate = 0;
|
||||
|
||||
return itstate;
|
||||
}
|
||||
|
||||
/* Find the next PC after the current instruction executes. In some
|
||||
cases we can not statically determine the answer (see the IT state
|
||||
handling in this function); in that case, a breakpoint may be
|
||||
inserted in addition to the returned PC, which will be used to set
|
||||
another breakpoint by our caller. */
|
||||
|
||||
static CORE_ADDR
|
||||
thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
|
||||
unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */
|
||||
unsigned short inst1;
|
||||
CORE_ADDR nextpc = pc + 2; /* default is next instruction */
|
||||
unsigned long offset;
|
||||
ULONGEST status, it;
|
||||
ULONGEST status, itstate;
|
||||
|
||||
inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
|
||||
|
||||
@ -2279,18 +2312,100 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
|
||||
block is active. These bits read as zero on earlier
|
||||
processors. */
|
||||
status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
|
||||
it = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
|
||||
itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
|
||||
|
||||
/* On GNU/Linux, where this routine is used, we use an undefined
|
||||
instruction as a breakpoint. Unlike BKPT, IT can disable execution
|
||||
of the undefined instruction. So we might miss the breakpoint! */
|
||||
if (((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0) || (it & 0x0f))
|
||||
error (_("Stepping through Thumb-2 IT blocks is not yet supported"));
|
||||
/* If-Then handling. On GNU/Linux, where this routine is used, we
|
||||
use an undefined instruction as a breakpoint. Unlike BKPT, IT
|
||||
can disable execution of the undefined instruction. So we might
|
||||
miss the breakpoint if we set it on a skipped conditional
|
||||
instruction. Because conditional instructions can change the
|
||||
flags, affecting the execution of further instructions, we may
|
||||
need to set two breakpoints. */
|
||||
|
||||
if (it & 0x0f)
|
||||
if (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL)
|
||||
{
|
||||
if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
|
||||
{
|
||||
/* An IT instruction. Because this instruction does not
|
||||
modify the flags, we can accurately predict the next
|
||||
executed instruction. */
|
||||
itstate = inst1 & 0x00ff;
|
||||
pc += thumb_insn_size (inst1);
|
||||
|
||||
while (itstate != 0 && ! condition_true (itstate >> 4, status))
|
||||
{
|
||||
inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
|
||||
pc += thumb_insn_size (inst1);
|
||||
itstate = thumb_advance_itstate (itstate);
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
else if (itstate != 0)
|
||||
{
|
||||
/* We are in a conditional block. Check the condition. */
|
||||
if (! condition_true (itstate >> 4, status))
|
||||
{
|
||||
/* Advance to the next executed instruction. */
|
||||
pc += thumb_insn_size (inst1);
|
||||
itstate = thumb_advance_itstate (itstate);
|
||||
|
||||
while (itstate != 0 && ! condition_true (itstate >> 4, status))
|
||||
{
|
||||
inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
|
||||
pc += thumb_insn_size (inst1);
|
||||
itstate = thumb_advance_itstate (itstate);
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
else if ((itstate & 0x0f) == 0x08)
|
||||
{
|
||||
/* This is the last instruction of the conditional
|
||||
block, and it is executed. We can handle it normally
|
||||
because the following instruction is not conditional,
|
||||
and we must handle it normally because it is
|
||||
permitted to branch. Fall through. */
|
||||
}
|
||||
else
|
||||
{
|
||||
int cond_negated;
|
||||
|
||||
/* There are conditional instructions after this one.
|
||||
If this instruction modifies the flags, then we can
|
||||
not predict what the next executed instruction will
|
||||
be. Fortunately, this instruction is architecturally
|
||||
forbidden to branch; we know it will fall through.
|
||||
Start by skipping past it. */
|
||||
pc += thumb_insn_size (inst1);
|
||||
itstate = thumb_advance_itstate (itstate);
|
||||
|
||||
/* Set a breakpoint on the following instruction. */
|
||||
gdb_assert ((itstate & 0x0f) != 0);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, pc);
|
||||
cond_negated = (itstate >> 4) & 1;
|
||||
|
||||
/* Skip all following instructions with the same
|
||||
condition. If there is a later instruction in the IT
|
||||
block with the opposite condition, set the other
|
||||
breakpoint there. If not, then set a breakpoint on
|
||||
the instruction after the IT block. */
|
||||
do
|
||||
{
|
||||
inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
|
||||
pc += thumb_insn_size (inst1);
|
||||
itstate = thumb_advance_itstate (itstate);
|
||||
}
|
||||
while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
|
||||
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (itstate & 0x0f)
|
||||
{
|
||||
/* We are in a conditional block. Check the condition. */
|
||||
int cond = it >> 4;
|
||||
int cond = itstate >> 4;
|
||||
|
||||
if (! condition_true (cond, status))
|
||||
{
|
||||
@ -2301,6 +2416,8 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
|
||||
else
|
||||
return pc + 2;
|
||||
}
|
||||
|
||||
/* Otherwise, handle the instruction normally. */
|
||||
}
|
||||
|
||||
if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
|
||||
@ -4749,10 +4866,29 @@ static const unsigned char *
|
||||
arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
|
||||
|
||||
if (arm_pc_is_thumb (*pcptr))
|
||||
{
|
||||
*pcptr = UNMAKE_THUMB_ADDR (*pcptr);
|
||||
|
||||
/* If we have a separate 32-bit breakpoint instruction for Thumb-2,
|
||||
check whether we are replacing a 32-bit instruction. */
|
||||
if (tdep->thumb2_breakpoint != NULL)
|
||||
{
|
||||
gdb_byte buf[2];
|
||||
if (target_read_memory (*pcptr, buf, 2) == 0)
|
||||
{
|
||||
unsigned short inst1;
|
||||
inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
|
||||
if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
|
||||
{
|
||||
*lenptr = tdep->thumb2_breakpoint_size;
|
||||
return tdep->thumb2_breakpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*lenptr = tdep->thumb_breakpoint_size;
|
||||
return tdep->thumb_breakpoint;
|
||||
}
|
||||
@ -4763,6 +4899,20 @@ arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
arm_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
|
||||
int *kindptr)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||
|
||||
arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
|
||||
|
||||
if (arm_pc_is_thumb (*pcptr) && *kindptr == 4)
|
||||
/* The documented magic value for a 32-bit Thumb-2 breakpoint, so
|
||||
that this is not confused with a 32-bit ARM breakpoint. */
|
||||
*kindptr = 3;
|
||||
}
|
||||
|
||||
/* Extract from an array REGBUF containing the (raw) register state a
|
||||
function return value of type TYPE, and copy that, in virtual
|
||||
format, into VALBUF. */
|
||||
@ -6091,6 +6241,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
|
||||
/* Breakpoint manipulation. */
|
||||
set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc);
|
||||
set_gdbarch_remote_breakpoint_from_pc (gdbarch,
|
||||
arm_remote_breakpoint_from_pc);
|
||||
|
||||
/* Information about registers, etc. */
|
||||
set_gdbarch_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM); /* ??? */
|
||||
|
@ -167,9 +167,16 @@ struct gdbarch_tdep
|
||||
|
||||
const char *arm_breakpoint; /* Breakpoint pattern for an ARM insn. */
|
||||
int arm_breakpoint_size; /* And its size. */
|
||||
const char *thumb_breakpoint; /* Breakpoint pattern for an ARM insn. */
|
||||
const char *thumb_breakpoint; /* Breakpoint pattern for a Thumb insn. */
|
||||
int thumb_breakpoint_size; /* And its size. */
|
||||
|
||||
/* If the Thumb breakpoint is an undefined instruction (which is
|
||||
affected by IT blocks) rather than a BKPT instruction (which is
|
||||
not), then we need a 32-bit Thumb breakpoint to preserve the
|
||||
instruction count in IT blocks. */
|
||||
const char *thumb2_breakpoint;
|
||||
int thumb2_breakpoint_size;
|
||||
|
||||
int jb_pc; /* Offset to PC value in jump buffer.
|
||||
If this is negative, longjmp support
|
||||
will be disabled. */
|
||||
|
@ -1,3 +1,9 @@
|
||||
2010-02-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* linux-arm-low.c (thumb_breakpoint_len): Delete.
|
||||
(thumb2_breakpoint): New.
|
||||
(arm_breakpoint_at): Check for Thumb-2 breakpoints.
|
||||
|
||||
2010-01-29 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* linux-low.c (get_stop_pc): Check for SIGTRAP.
|
||||
|
@ -203,7 +203,7 @@ arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
|
||||
static const unsigned long arm_breakpoint = 0xef9f0001;
|
||||
#define arm_breakpoint_len 4
|
||||
static const unsigned short thumb_breakpoint = 0xde01;
|
||||
#define thumb_breakpoint_len 2
|
||||
static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
|
||||
|
||||
/* For new EABI binaries. We recognize it regardless of which ABI
|
||||
is used for gdbserver, so single threaded debugging should work
|
||||
@ -227,6 +227,13 @@ arm_breakpoint_at (CORE_ADDR where)
|
||||
(*the_target->read_memory) (where, (unsigned char *) &insn, 2);
|
||||
if (insn == thumb_breakpoint)
|
||||
return 1;
|
||||
|
||||
if (insn == thumb2_breakpoint[0])
|
||||
{
|
||||
(*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
|
||||
if (insn == thumb2_breakpoint[1])
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,3 +1,7 @@
|
||||
2010-02-01 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* gdb.arch/thumb2-it.S, gdb.arch/thumb2-it.exp: New files.
|
||||
|
||||
2010-01-29 Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* gdb.base/call-strs.exp, gdb.base/default.exp,
|
||||
|
139
gdb/testsuite/gdb.arch/thumb2-it.S
Normal file
139
gdb/testsuite/gdb.arch/thumb2-it.S
Normal file
@ -0,0 +1,139 @@
|
||||
/* Thumb-2 IT blocks test program.
|
||||
|
||||
Copyright 2010 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/>. */
|
||||
|
||||
.syntax unified
|
||||
.text
|
||||
.p2align 2
|
||||
.code 16
|
||||
|
||||
#ifndef __thumb2__
|
||||
|
||||
.type main,%function
|
||||
.thumb_func
|
||||
.globl main
|
||||
main:
|
||||
mov r0, #0
|
||||
bx lr @ No Thumb-2
|
||||
|
||||
#else
|
||||
|
||||
.type main,%function
|
||||
.thumb_func
|
||||
.globl main
|
||||
main:
|
||||
mov r0, #0
|
||||
bx lr @ Thumb-2 OK
|
||||
|
||||
@ One conditional instruction, executed.
|
||||
.type it_1,%function
|
||||
.thumb_func
|
||||
it_1:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
it eq @ IT instruction, Expected == 1
|
||||
addeq r0, #1 @ Reached
|
||||
bx lr @ Done
|
||||
|
||||
@ One conditional instruction, skipped.
|
||||
.type it_2,%function
|
||||
.thumb_func
|
||||
it_2:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
it ne @ IT instruction, Expected == 0
|
||||
addne r0, #1 @ Not reached
|
||||
bx lr @ Done, Check $r0 == 0
|
||||
|
||||
@ Block of four, alternating, starting with executed.
|
||||
.type it_3,%function
|
||||
.thumb_func
|
||||
it_3:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
itete ge @ IT instruction, Expected == 2
|
||||
addge r0, #1 @ Reached
|
||||
addlt r0, #2 @ Not reached
|
||||
addge r0, #4 @ Reached
|
||||
addlt r0, #8 @ Not reached
|
||||
bx lr @ Done, Check $r0 == 5
|
||||
|
||||
@ Block of four, changing flags.
|
||||
.type it_4,%function
|
||||
.thumb_func
|
||||
it_4:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
itttt ge @ IT instruction, Expected == 2
|
||||
addge r0, #1 @ Reached
|
||||
cmpge r0, #10 @ Reached
|
||||
addge r0, #4 @ Not reached
|
||||
addge r0, #8 @ Not reached
|
||||
bx lr @ Done, Check $r0 == 1
|
||||
|
||||
@ Block of two, ending with taken branch.
|
||||
.type it_5,%function
|
||||
.thumb_func
|
||||
it_5:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
itt ge @ IT instruction, Expected == 2
|
||||
addge r0, #1 @ Reached
|
||||
bge .L5 @ Reached
|
||||
add r0, #2 @ Never reached
|
||||
.L5: bx lr @ Done, Check $r0 == 1
|
||||
|
||||
@ Block of two, ending with untaken branch.
|
||||
.type it_6,%function
|
||||
.thumb_func
|
||||
it_6:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
ite ge @ IT instruction, Expected == 2
|
||||
addge r0, #1 @ Reached
|
||||
blt .L6 @ Not reached
|
||||
add r0, #2 @ Reached
|
||||
.L6: bx lr @ Done, Check $r0 == 3
|
||||
|
||||
@ Block of four, taken, of different sizes
|
||||
.type it_7,%function
|
||||
.thumb_func
|
||||
it_7:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
itttt ge @ IT instruction, Expected == 4
|
||||
addge.n r0, #1 @ Reached
|
||||
addge.w r0, #2 @ Reached
|
||||
addge.n r0, #4 @ Reached
|
||||
addge.w r0, #8 @ Reached
|
||||
bx lr @ Done, Check $r0 == 15
|
||||
|
||||
@ Block of four, only first executed.
|
||||
.type it_3,%function
|
||||
.thumb_func
|
||||
it_8:
|
||||
mov r0, #0 @ Setup
|
||||
cmp r0, #0 @ Setup
|
||||
iteee ge @ IT instruction, Expected == 1
|
||||
addge r0, #1 @ Reached
|
||||
addlt r0, #2 @ Not reached
|
||||
addlt r0, #4 @ Not reached
|
||||
addlt r0, #8 @ Not reached
|
||||
bx lr @ Done, Check $r0 == 1
|
||||
|
||||
#endif /* __thumb2__ */
|
140
gdb/testsuite/gdb.arch/thumb2-it.exp
Normal file
140
gdb/testsuite/gdb.arch/thumb2-it.exp
Normal file
@ -0,0 +1,140 @@
|
||||
# Copyright 2010 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 over Thumb-2 IT blocks.
|
||||
|
||||
if {![istarget arm*-*eabi*]} then {
|
||||
verbose "Skipping Thumb-2 tests."
|
||||
return
|
||||
}
|
||||
|
||||
set testfile "thumb2-it"
|
||||
set srcfile ${testfile}.S
|
||||
set binfile ${objdir}/${subdir}/${testfile}
|
||||
|
||||
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable debug] != "" } {
|
||||
untested thumb2-it.exp
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_exit
|
||||
gdb_start
|
||||
gdb_reinitialize_dir $srcdir/$subdir
|
||||
gdb_load ${binfile}
|
||||
|
||||
if ![runto_main] then {
|
||||
untested thumb2-it.exp
|
||||
return -1
|
||||
}
|
||||
|
||||
# Make sure that the compiler options allow Thumb-2.
|
||||
gdb_test_multiple "list" "list main" {
|
||||
-re ".*@ No Thumb-2.*$gdb_prompt $" {
|
||||
pass "list main"
|
||||
untested thumb2-it.exp
|
||||
return -1
|
||||
}
|
||||
-re ".*@ Thumb-2 OK.*$gdb_prompt $" {
|
||||
pass "list main"
|
||||
}
|
||||
}
|
||||
|
||||
proc test_it_block { func } {
|
||||
global gdb_prompt
|
||||
global software_step
|
||||
|
||||
if { ! [gdb_breakpoint "*${func}"] } {
|
||||
unresolved "$func, IT block tests"
|
||||
return
|
||||
}
|
||||
|
||||
gdb_test "call ${func}()" "Breakpoint.*@ Setup.*" "$func, call"
|
||||
|
||||
set expected 0
|
||||
set reached 0
|
||||
set steps 0
|
||||
set ok 1
|
||||
while { $ok } {
|
||||
set ok 0
|
||||
set msg "$func, stepi $steps"
|
||||
gdb_test_multiple "stepi" "$msg" {
|
||||
-re ".*@ Setup.*$gdb_prompt $" {
|
||||
pass "$msg"
|
||||
set ok 1
|
||||
}
|
||||
-re ".*@ IT instruction, Expected == (\[0-9\]*)\r\n$gdb_prompt $" {
|
||||
set expected $expect_out(1,string)
|
||||
pass "$msg"
|
||||
set ok 1
|
||||
}
|
||||
-re ".*@ Reached.*$gdb_prompt $" {
|
||||
incr reached
|
||||
pass "$msg"
|
||||
set ok 1
|
||||
if { [regexp {@ Reached, Set ([^\r\n]*)\r\n} $expect_out(0,string) dummy change] } {
|
||||
gdb_test "set $change" "" "$func, set $change"
|
||||
}
|
||||
}
|
||||
-re ".*@ Not reached.*$gdb_prompt $" {
|
||||
# An instruction in an IT block whose predicate is false when
|
||||
# we reach it. If using software single step, we should not
|
||||
# stop here.
|
||||
if { $software_step } {
|
||||
fail "$msg"
|
||||
} else {
|
||||
pass "$msg"
|
||||
set ok 1
|
||||
}
|
||||
}
|
||||
-re ".*@ Never reached.*$gdb_prompt $" {
|
||||
# An instruction that should be branched over.
|
||||
fail "$msg"
|
||||
}
|
||||
-re ".*@ Done.*$gdb_prompt $" {
|
||||
pass "$msg"
|
||||
if { $reached == $expected } {
|
||||
pass "$func, correct instructions reached"
|
||||
} else {
|
||||
fail "$func, correct instructions reached"
|
||||
}
|
||||
if { [regexp {@ Done, Check ([^\r\n]*)\r\n} $expect_out(0,string) dummy check] } {
|
||||
gdb_test "print $check" ".* = 1" "$func, $check"
|
||||
}
|
||||
}
|
||||
}
|
||||
if { ! $ok } {
|
||||
break
|
||||
}
|
||||
incr steps
|
||||
continue
|
||||
}
|
||||
|
||||
gdb_test "continue" "" "$func, continue"
|
||||
return
|
||||
}
|
||||
|
||||
# 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
|
||||
# step, then GDB will stop at such instructions.
|
||||
if { [istarget arm*-linux*] } {
|
||||
set software_step 1
|
||||
} else {
|
||||
set software_step 0
|
||||
}
|
||||
|
||||
for { set i 1 } { $i <= 8 } { incr i } {
|
||||
test_it_block it_${i}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user