diff options
author | Pedro Alves <palves@redhat.com> | 2015-10-30 16:00:43 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2015-10-30 16:00:43 +0000 |
commit | d35ae83384324ec7a03a56600174b5e925510b74 (patch) | |
tree | 6e5f9330bb0c150341e8282513f280b5b3e47add /gdb/infrun.c | |
parent | 4081c0f1222810746068579c6fbbcd8a9b59cbb8 (diff) | |
download | gdb-d35ae83384324ec7a03a56600174b5e925510b74.zip gdb-d35ae83384324ec7a03a56600174b5e925510b74.tar.gz gdb-d35ae83384324ec7a03a56600174b5e925510b74.tar.bz2 |
Don't displaced step when there's a breakpoint in the scratch pad range
Assuming displaced stepping is enabled, and a breakpoint is set in the
memory region of the scratch pad, things break. One of two cases can
happen:
#1 - The breakpoint wasn't inserted yet (all threads were stopped), so
after setting up the displaced stepping scratch pad with the
adjusted copy of the instruction we're trying to single-step, we
insert the breakpoint, which corrupts the scratch pad, and the
inferior executes the wrong instruction. (Example below.)
This is clearly unacceptable.
#2 - The breakpoint was already inserted, so setting up the displaced
stepping scratch pad overwrites the breakpoint. This is OK in
the sense that we already assume that no thread is going to
executes the code in the scratch pad range (after initial
startup) anyway.
This commit addresses both cases by simply punting on displaced
stepping if we have a breakpoint in the scratch pad range.
The #1 case above explains a few regressions exposed by the AS/NS
series on x86:
Running ./gdb.dwarf2/callframecfa.exp ...
FAIL: gdb.dwarf2/callframecfa.exp: set display for call-frame-cfa
FAIL: gdb.dwarf2/callframecfa.exp: step 1 for call-frame-cfa
FAIL: gdb.dwarf2/callframecfa.exp: step 2 for call-frame-cfa
FAIL: gdb.dwarf2/callframecfa.exp: step 3 for call-frame-cfa
FAIL: gdb.dwarf2/callframecfa.exp: step 4 for call-frame-cfa
Running ./gdb.dwarf2/typeddwarf.exp ...
FAIL: gdb.dwarf2/typeddwarf.exp: continue to breakpoint: continue to typeddwarf.c:53
FAIL: gdb.dwarf2/typeddwarf.exp: check value of x at typeddwarf.c:53
FAIL: gdb.dwarf2/typeddwarf.exp: check value of y at typeddwarf.c:53
FAIL: gdb.dwarf2/typeddwarf.exp: check value of z at typeddwarf.c:53
FAIL: gdb.dwarf2/typeddwarf.exp: continue to breakpoint: continue to typeddwarf.c:73
FAIL: gdb.dwarf2/typeddwarf.exp: check value of w at typeddwarf.c:73
FAIL: gdb.dwarf2/typeddwarf.exp: check value of x at typeddwarf.c:73
FAIL: gdb.dwarf2/typeddwarf.exp: check value of y at typeddwarf.c:73
FAIL: gdb.dwarf2/typeddwarf.exp: check value of z at typeddwarf.c:73
Enabling "maint set target-non-stop on" implies displaced stepping
enabled as well, and it's the latter that's to blame here. We can see
the same failures with "maint set target-non-stop off + set displaced
on".
Diffing (good/bad) gdb.log for callframecfa.exp shows:
@@ -99,29 +99,29 @@ Breakpoint 2 at 0x80481b0: file q.c, lin
continue
Continuing.
-Breakpoint 2, func (arg=77) at q.c:2
+Breakpoint 2, func (arg=52301) at q.c:2
2 in q.c
(gdb) PASS: gdb.dwarf2/callframecfa.exp: continue to breakpoint: continue to breakpoint for call-frame-cfa
display arg
-1: arg = 77
-(gdb) PASS: gdb.dwarf2/callframecfa.exp: set display for call-frame-cfa
+1: arg = 52301
+(gdb) FAIL: gdb.dwarf2/callframecfa.exp: set display for call-frame-cfa
The problem is here, when setting up the func call:
Breakpoint 1, main (argc=-13345, argv=0x0) at q.c:7
7 in q.c
(gdb) disassemble
Dump of assembler code for function main:
0x080481bb <+0>: push %ebp
0x080481bc <+1>: mov %esp,%ebp
0x080481be <+3>: sub $0x4,%esp
=> 0x080481c1 <+6>: movl $0x4d,(%esp)
0x080481c8 <+13>: call 0x80481b0 <func>
0x080481cd <+18>: leave
0x080481ce <+19>: ret
End of assembler dump.
(gdb) disassemble /r
Dump of assembler code for function main:
0x080481bb <+0>: 55 push %ebp
0x080481bc <+1>: 89 e5 mov %esp,%ebp
0x080481be <+3>: 83 ec 04 sub $0x4,%esp
=> 0x080481c1 <+6>: c7 04 24 4d 00 00 00 movl $0x4d,(%esp)
0x080481c8 <+13>: e8 e3 ff ff ff call 0x80481b0 <func>
0x080481cd <+18>: c9 leave
0x080481ce <+19>: c3 ret
End of assembler dump.
Note the breakpoint at main is set at 0x080481c1. Right at the
instruction that sets up func's argument. Executing that instruction
should write 0x4d to the address pointed at by $esp. However, if we
stepi, the program manages to write 52301/0xcc4d there instead (0xcc
is int3, the x86 breakpoint instruction), because the breakpoint
address is 4 bytes inside the scratch pad location, which is
0x080481bd:
(gdb) p 0x080481c1 - 0x080481bd
$1 = 4
IOW, instead of executing:
"c7 04 24 4d 00 00 00" [ movl $0x4d,(%esp) ]
the inferior executes:
"c7 04 24 4d cc 00 00" [ movl $0xcc4d,(%esp) ]
gdb/ChangeLog:
2015-10-30 Pedro Alves <palves@redhat.com>
* breakpoint.c (breakpoint_in_range_p)
(breakpoint_location_address_range_overlap): New functions.
* breakpoint.h (breakpoint_in_range_p): New declaration.
* infrun.c (displaced_step_prepare_throw): If there's a breakpoint
in the scratch pad range, don't displaced step.
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r-- | gdb/infrun.c | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c index 917f9be..ef4ccb4 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1729,6 +1729,7 @@ displaced_step_prepare_throw (ptid_t ptid) struct thread_info *tp = find_thread_ptid (ptid); struct regcache *regcache = get_thread_regcache (ptid); struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct address_space *aspace = get_regcache_aspace (regcache); CORE_ADDR original, copy; ULONGEST len; struct displaced_step_closure *closure; @@ -1784,6 +1785,28 @@ displaced_step_prepare_throw (ptid_t ptid) copy = gdbarch_displaced_step_location (gdbarch); len = gdbarch_max_insn_length (gdbarch); + if (breakpoint_in_range_p (aspace, copy, len)) + { + /* There's a breakpoint set in the scratch pad location range + (which is usually around the entry point). We'd either + install it before resuming, which would overwrite/corrupt the + scratch pad, or if it was already inserted, this displaced + step would overwrite it. The latter is OK in the sense that + we already assume that no thread is going to execute the code + in the scratch pad range (after initial startup) anyway, but + the former is unacceptable. Simply punt and fallback to + stepping over this breakpoint in-line. */ + if (debug_displaced) + { + fprintf_unfiltered (gdb_stdlog, + "displaced: breakpoint set in scratch pad. " + "Stepping over breakpoint in-line instead.\n"); + } + + do_cleanups (old_cleanups); + return -1; + } + /* Save the original contents of the copy area. */ displaced->step_saved_copy = (gdb_byte *) xmalloc (len); ignore_cleanups = make_cleanup (free_current_contents, |