diff options
author | Pedro Alves <palves@redhat.com> | 2014-11-12 10:10:49 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2014-11-12 10:39:00 +0000 |
commit | af48d08f97fa678571a42be35a8a77b61e36d7d7 (patch) | |
tree | 37b1528627c562f4e573a258021fc3de92c7476e /gdb/testsuite/gdb.arch | |
parent | 1a853c5224e2b8fedfac6d029365522b83080b40 (diff) | |
download | gdb-af48d08f97fa678571a42be35a8a77b61e36d7d7.zip gdb-af48d08f97fa678571a42be35a8a77b61e36d7d7.tar.gz gdb-af48d08f97fa678571a42be35a8a77b61e36d7d7.tar.bz2 |
fix skipping permanent breakpoints
The gdb.arch/i386-bp_permanent.exp test is currently failing an
assertion recently added:
(gdb) stepi
../../src/gdb/infrun.c:2237: internal-error: resume: Assertion `sig != GDB_SIGNAL_0' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)
FAIL: gdb.arch/i386-bp_permanent.exp: Single stepping past permanent breakpoint. (GDB internal error)
The assertion expects that the only reason we currently need to step a
breakpoint instruction is when we have a signal to deliver. But when
stepping a permanent breakpoint (with or without a signal) we also
reach this code.
The assertion is correct and the permanent breakpoints skipping code
is wrong.
Consider the case of the user doing "step/stepi" when stopped at a
permanent breakpoint. GDB's `resume' calls the
gdbarch_skip_permanent_breakpoint hook and then happily continues
stepping:
/* Normally, by the time we reach `resume', the breakpoints are either
removed or inserted, as appropriate. The exception is if we're sitting
at a permanent breakpoint; we need to step over it, but permanent
breakpoints can't be removed. So we have to test for it here. */
if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here)
{
gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
}
But since gdbarch_skip_permanent_breakpoint already advanced the PC
manually, this ends up executing the instruction that is _after_ the
breakpoint instruction. The user-visible result is that a single-step
steps two instructions.
The gdb.arch/i386-bp_permanent.exp test is actually ensuring that
that's indeed how things work. It runs to an int3 instruction, does
"stepi", and checks that "leave" was executed with that "stepi". Like
this:
(gdb) b *0x0804848c
Breakpoint 2 at 0x804848c
(gdb) c
Continuing.
Breakpoint 2, 0x0804848c in standard ()
(gdb) disassemble
Dump of assembler code for function standard:
0x08048488 <+0>: push %ebp
0x08048489 <+1>: mov %esp,%ebp
0x0804848b <+3>: push %edi
=> 0x0804848c <+4>: int3
0x0804848d <+5>: leave
0x0804848e <+6>: ret
0x0804848f <+7>: nop
(gdb) si
0x0804848e in standard ()
(gdb) disassemble
Dump of assembler code for function standard:
0x08048488 <+0>: push %ebp
0x08048489 <+1>: mov %esp,%ebp
0x0804848b <+3>: push %edi
0x0804848c <+4>: int3
0x0804848d <+5>: leave
=> 0x0804848e <+6>: ret
0x0804848f <+7>: nop
End of assembler dump.
(gdb)
One would instead expect that a stepi at 0x0804848c stops at
0x0804848d, _before_ the "leave" is executed. This commit changes GDB
this way. Care is taken to make stepping into a signal handler when
the step starts at a permanent breakpoint instruction work correctly.
The patch adjusts gdb.arch/i386-bp_permanent.exp in this direction,
and also makes it work on x86_64 (currently it only works on i*86).
The patch also adds a new gdb.base/bp-permanent.exp test that
exercises many different code paths related to stepping permanent
breakpoints, including the stepping with signals cases. The test uses
"hack/trick" to make it work on all (or most) platforms -- it doesn't
really hard code a breakpoint instruction.
Tested on x86_64 Fedora 20, native and gdbserver.
gdb/
2014-11-12 Pedro Alves <palves@redhat.com>
* infrun.c (resume): Clear the thread's 'stepped_breakpoint' flag.
Rewrite stepping over a permanent breakpoint.
(thread_still_needs_step_over, proceed): Don't set
stepping_over_breakpoint for permanent breakpoints.
(handle_signal_stop): Don't clear stepped_breakpoint. Also pull
single-step breakpoints out of the target on hardware step
targets.
(process_event_stop_test): If stepping a permanent breakpoint
doesn't hit the step-resume breakpoint, delete the step-resume
breakpoint.
(switch_back_to_stepped_thread): Also check if the stepped thread
has advanced already on hardware step targets.
(currently_stepping): Return true if the thread stepped a
breakpoint.
gdb/testsuite/
2014-11-12 Pedro Alves <palves@redhat.com>
* gdb.arch/i386-bp_permanent.c: New file.
* gdb.arch/i386-bp_permanent.exp: Don't skip on x86_64.
(srcfile): Set to i386-bp_permanent.c.
(top level): Adjust to work in both 32-bit and 64-bit modes. Test
that stepi does not execute the 'leave' instruction, instead of
testing it does execute.
* gdb.base/bp-permanent.c: New file.
* gdb.base/bp-permanent.exp: New file.
Diffstat (limited to 'gdb/testsuite/gdb.arch')
-rw-r--r-- | gdb/testsuite/gdb.arch/i386-bp_permanent.c | 57 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/i386-bp_permanent.exp | 49 |
2 files changed, 78 insertions, 28 deletions
diff --git a/gdb/testsuite/gdb.arch/i386-bp_permanent.c b/gdb/testsuite/gdb.arch/i386-bp_permanent.c new file mode 100644 index 0000000..dc36d89 --- /dev/null +++ b/gdb/testsuite/gdb.arch/i386-bp_permanent.c @@ -0,0 +1,57 @@ +/* Copyright (C) 2003-2014 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/>. */ + +#ifdef SYMBOL_PREFIX +#define SYMBOL(str) SYMBOL_PREFIX #str +#else +#define SYMBOL(str) #str +#endif + +int +main (void) +{ + standard (); + return 0; +} + +/* A normal prologue. */ + +#ifdef __x86_64__ +asm(".text\n" + " .align 8\n" + SYMBOL (standard) ":\n" + " push %rbp\n" + " mov %rsp, %rbp\n" + " push %rdi\n" + " int3\n" + " leaveq\n" + " retq\n"); + +#else + +asm(".text\n" + " .align 8\n" + " .global " SYMBOL(standard) "\n" + SYMBOL (standard) ":\n" + " pushl %ebp\n" + " movl %esp, %ebp\n" + " pushl %edi\n" + " int3\n" + " leave\n" + " ret\n"); + +#endif diff --git a/gdb/testsuite/gdb.arch/i386-bp_permanent.exp b/gdb/testsuite/gdb.arch/i386-bp_permanent.exp index 6be1626..833b4ed 100644 --- a/gdb/testsuite/gdb.arch/i386-bp_permanent.exp +++ b/gdb/testsuite/gdb.arch/i386-bp_permanent.exp @@ -18,13 +18,13 @@ # Test stepping over permanent breakpoints on i386. -if { ![is_x86_like_target] } then { +if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } then { verbose "Skipping skip over permanent breakpoint on i386 tests." return } set testfile "i386-bp_permanent" -set srcfile i386-prologue.c +set srcfile i386-bp_permanent.c set binfile ${objdir}/${subdir}/${testfile} # some targets have leading underscores on assembly symbols. @@ -51,14 +51,12 @@ if ![runto_main] then { return -1 } -set function standard +set function "standard" set retcode [gdb_test_multiple "disassemble $function" "Disassemble function '$function'" { - -re ".*($hex) <\\+0>.*($hex) <\\+4>.*($hex) <\\+5>.*($hex) <\\+6>.*$gdb_prompt $" { - set function_start $expect_out(1,string) - set address $expect_out(2,string) - set address1 $expect_out(3,string) - set address2 $expect_out(4,string) + -re "($hex) <\\+0>.*($hex) <\\+$decimal>.*int3.*($hex) <\\+$decimal>.*leave.*$gdb_prompt $" { + set address_bp $expect_out(2,string) + set address_after_bp $expect_out(3,string) } }] @@ -67,30 +65,25 @@ if {$retcode != 0} { return -1 } -gdb_breakpoint "*$function_start" +gdb_breakpoint "*$address_bp" -gdb_breakpoint "*$address" +gdb_test "continue" "Breakpoint .*, $address_bp in $function.*" \ + "stop at permanent breakpoint" -gdb_test "continue" "Breakpoint .*, $function_start in $function.*" \ - "Stop at the '$function' start breakpoint (fetching esp)." - -# We want to fetch esp at the start of '$function' function to make sure -# skip_permanent_breakpoint implementation really skips only the perm. -# breakpoint. If, for whatever reason, 'leave' instruction doesn't get -# executed, esp will not have this value. -set start_esp 0 -gdb_test_multiple "print \$esp" "Fetch esp value." { +# We want to fetch the stack pointer at the start of '$function' +# function to make sure the skip_permanent_breakpoint implementation +# really skips only the permanent breakpoint. If, for whatever +# reason, the 'leave' instruction executes, the stack pointer will not +# have this value. +set start_sp 0 +gdb_test_multiple "print \$sp" "fetch stack pointer value" { -re "\\\$1.*($hex).*$gdb_prompt $" { - set start_esp $expect_out(1,string) + set start_sp $expect_out(1,string) } } -gdb_test "continue" "Breakpoint .*, $address in $function.*" \ - "Stop at permanent breakpoint." - -gdb_test "stepi" "$address1|$address2 in $function.*" \ - "Single stepping past permanent breakpoint." - -gdb_test "print \$esp" ".*$start_esp.*" \ - "ESP value does not match - step_permanent_breakpoint wrong." +gdb_test "stepi" "$address_after_bp in $function.*" \ + "single-step past permanent breakpoint" +gdb_test "print \$sp" ".*$start_sp.*" \ + "stack pointer value matches" |