Stale breakpoint instructions, spurious SIGTRAPS.
Without the code portion of the patch, we get these failures: FAIL: gdb.base/break-unload-file.exp: always-inserted on: break: continue FAIL: gdb.base/break-unload-file.exp: always-inserted on: hbreak: continue FAIL: gdb.base/sym-file.exp: stale bkpts: continue to breakpoint: end here They all looks like random SIGTRAPs: continue Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. 0x0000000000400541 in foo () at ../../../src/gdb/testsuite/gdb.base/break-unload-file.c:21 21 } (gdb) FAIL: gdb.base/break-unload-file.exp: always-inserted on: break: continue (This is a regression caused by the remove-symbol-file command series.) break-unload-file.exp is about having breakpoints inserted, and then doing "file". I caught this while writing a test that does "file PROGRAM", while PROGRAM was already loaded, which internally does "file" first, because I wanted to force a breakpoint_re_set, but the test is more explicit in case GDB ever optimizes out that re-set. The problem is that unloading the file with "file" ends up in disable_breakpoints_in_freed_objfile, which marks all breakpoint locations of the objfile as both shlib_disabled, _and_ clears the inserted flag, without actually removing the breakpoints from the inferior. Now, usually, in all-stop, breakpoints will already be removed from the inferior before the user can issue the "file" command, but, with non-stop, or breakpoints always-inserted on mode, breakpoints stay inserted even while the user has the prompt. In the latter case, then, if we let the program continue, and it executes the address where we had previously set the breakpoint, it'll actually execute the breakpoint instruction that we left behind... Now, one issue is that the intent of disable_breakpoints_in_freed_objfile is really to handle the unloading of OBJF_USERLOADED objfiles. These are objfiles that were added with add-symbol-file and that are removed with remove-symbol-file. "add-symbol-file"'s docs in the manual clearly say these commands are used to let GDB know about dynamically loaded code: You would use this command when @var{filename} has been dynamically loaded (by some other means) into the program that is running. Similarly, the online help says: (gdb) help add-symbol-file Load symbols from FILE, assuming FILE has been dynamically loaded. So it makes sense to, like when shared libraries are unloaded through the generic solib machinery, mark the breakpoint locations as shlib_disabled. But, the "file" command is not about dynamically loaded code, it's about the main program. So the patch makes disable_breakpoints_in_freed_objfile skip all objfiles but OBJF_USERLOADED ones, thus skipping the main objfile. Then, the reason that disable_breakpoints_in_freed_objfile was clearing the inserted flag isn't clear, but likely to avoid breakpoint removal errors, assuming remove-symbol-file was called after the dynamic object was already unmapped from the inferior. In that case, it'd okay to simply clear the inserted flag, but not so if the user for example does remove-symbol-file to remove the library because he made a mistake in the library's address, and wants to re-do add-symbol-file with the correct address. To address all that, I propose an alternative implementation, that handles both cases. The patch includes changes to sym-file.exp to cover them. This implementation leaves the inserted flag alone, and handles breakpoint insertion/removal failure gracefully when the locations are in OBJF_USERLOADED objfiles, just like we handle insertion/removal failure gracefully for locations in shared libraries. To try to make sure we aren't patching back stale shadow memory contents into the inferior, in case the program mapped a different library at the same address where we had the breakpoint, without the user having had a chance of remove-symbol-file'ing before, this adds a new memory_validate_breakpoint function that checks if the breakpoint instruction is still in memory. ppc_linux_memory_remove_breakpoint does this unconditionally for all memory breakpoints, and questions whether memory_remove_breakpoint should be changed to do this for all breakpoints. Possibly yes, though I'm not certain, hence this baby-steps patch. Tested on x86_64 Fedora 17, native and gdbserver. gdb/ 2014-04-23 Pedro Alves <palves@redhat.com> * breakpoint.c (insert_bp_location): Tolerate errors if the breakpoint is set in a user-loaded objfile. (remove_breakpoint_1): Likewise. Also tolerate errors if the location is marked shlib_disabled. If the breakpoint is set in a user-loaded objfile is a GDB-side memory breakpoint, validate it before uninsertion. (disable_breakpoints_in_freed_objfile): Skip non-OBJF_USERLOADED objfiles. Don't clear the location's inserted flag. * mem-break.c (memory_validate_breakpoint): New function. * objfiles.c (userloaded_objfile_contains_address_p): New function. * objfiles.h (userloaded_objfile_contains_address_p): Declare. * target.h (memory_validate_breakpoint): New declaration. gdb/testsuite/ 2014-04-23 Pedro Alves <palves@redhat.com> * gdb.base/break-unload-file.c: New file. * gdb.base/break-unload-file.exp: New file. * gdb.base/sym-file-lib.c (baz): New function. * gdb.base/sym-file-loader.c (struct segment) <mapped_size>: New field. (load): Store the segment's mapped size. (unload): New function. (unload_shlib): New function. * gdb.base/sym-file-loader.h (unload_shlib): New declaration. * gdb.base/sym-file-main.c (main): Unload, and reload the library, set a breakpoint at baz, and call it. * gdb.base/sym-file.exp: New tests for stale breakpoint instructions.
This commit is contained in:
parent
076855f9e3
commit
08351840ea
14 changed files with 425 additions and 15 deletions
|
@ -1,3 +1,19 @@
|
||||||
|
2014-04-23 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
|
* breakpoint.c (insert_bp_location): Tolerate errors if the
|
||||||
|
breakpoint is set in a user-loaded objfile.
|
||||||
|
(remove_breakpoint_1): Likewise. Also tolerate errors if the
|
||||||
|
location is marked shlib_disabled. If the breakpoint is set in a
|
||||||
|
user-loaded objfile is a GDB-side memory breakpoint, validate it
|
||||||
|
before uninsertion. (disable_breakpoints_in_freed_objfile): Skip
|
||||||
|
non-OBJF_USERLOADED objfiles. Don't clear the location's inserted
|
||||||
|
flag.
|
||||||
|
* mem-break.c (memory_validate_breakpoint): New function.
|
||||||
|
* objfiles.c (userloaded_objfile_contains_address_p): New
|
||||||
|
function.
|
||||||
|
* objfiles.h (userloaded_objfile_contains_address_p): Declare.
|
||||||
|
* target.h (memory_validate_breakpoint): New declaration.
|
||||||
|
|
||||||
2014-04-23 Pedro Alves <palves@redhat.com>
|
2014-04-23 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
* breakpoint.c (insert_bp_location, remove_breakpoint_1): If
|
* breakpoint.c (insert_bp_location, remove_breakpoint_1): If
|
||||||
|
|
|
@ -2648,7 +2648,9 @@ insert_bp_location (struct bp_location *bl,
|
||||||
errors as memory errors. */
|
errors as memory errors. */
|
||||||
if ((bp_err == GENERIC_ERROR || bp_err == MEMORY_ERROR)
|
if ((bp_err == GENERIC_ERROR || bp_err == MEMORY_ERROR)
|
||||||
&& bl->loc_type == bp_loc_software_breakpoint
|
&& bl->loc_type == bp_loc_software_breakpoint
|
||||||
&& solib_name_from_address (bl->pspace, bl->address))
|
&& (solib_name_from_address (bl->pspace, bl->address)
|
||||||
|
|| userloaded_objfile_contains_address_p (bl->pspace,
|
||||||
|
bl->address)))
|
||||||
{
|
{
|
||||||
/* See also: disable_breakpoints_in_shlibs. */
|
/* See also: disable_breakpoints_in_shlibs. */
|
||||||
bl->shlib_disabled = 1;
|
bl->shlib_disabled = 1;
|
||||||
|
@ -3778,7 +3780,31 @@ remove_breakpoint_1 (struct bp_location *bl, insertion_state_t is)
|
||||||
|| !(section_is_overlay (bl->section)))
|
|| !(section_is_overlay (bl->section)))
|
||||||
{
|
{
|
||||||
/* No overlay handling: just remove the breakpoint. */
|
/* No overlay handling: just remove the breakpoint. */
|
||||||
val = bl->owner->ops->remove_location (bl);
|
|
||||||
|
/* If we're trying to uninsert a memory breakpoint that we
|
||||||
|
know is set in a dynamic object that is marked
|
||||||
|
shlib_disabled, then either the dynamic object was
|
||||||
|
removed with "remove-symbol-file" or with
|
||||||
|
"nosharedlibrary". In the former case, we don't know
|
||||||
|
whether another dynamic object might have loaded over the
|
||||||
|
breakpoint's address -- the user might well let us know
|
||||||
|
about it next with add-symbol-file (the whole point of
|
||||||
|
OBJF_USERLOADED is letting the user manually maintain a
|
||||||
|
list of dynamically loaded objects). If we have the
|
||||||
|
breakpoint's shadow memory, that is, this is a software
|
||||||
|
breakpoint managed by GDB, check whether the breakpoint
|
||||||
|
is still inserted in memory, to avoid overwriting wrong
|
||||||
|
code with stale saved shadow contents. Note that HW
|
||||||
|
breakpoints don't have shadow memory, as they're
|
||||||
|
implemented using a mechanism that is not dependent on
|
||||||
|
being able to modify the target's memory, and as such
|
||||||
|
they should always be removed. */
|
||||||
|
if (bl->shlib_disabled
|
||||||
|
&& bl->target_info.shadow_len != 0
|
||||||
|
&& !memory_validate_breakpoint (bl->gdbarch, &bl->target_info))
|
||||||
|
val = 0;
|
||||||
|
else
|
||||||
|
val = bl->owner->ops->remove_location (bl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3823,12 +3849,21 @@ remove_breakpoint_1 (struct bp_location *bl, insertion_state_t is)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In some cases, we might not be able to remove a breakpoint
|
/* In some cases, we might not be able to remove a breakpoint in
|
||||||
in a shared library that has already been removed, but we
|
a shared library that has already been removed, but we have
|
||||||
have not yet processed the shlib unload event. */
|
not yet processed the shlib unload event. Similarly for an
|
||||||
|
unloaded add-symbol-file object - the user might not yet have
|
||||||
|
had the chance to remove-symbol-file it. shlib_disabled will
|
||||||
|
be set if the library/object has already been removed, but
|
||||||
|
the breakpoint hasn't been uninserted yet, e.g., after
|
||||||
|
"nosharedlibrary" or "remove-symbol-file" with breakpoints
|
||||||
|
always-inserted mode. */
|
||||||
if (val
|
if (val
|
||||||
&& bl->loc_type == bp_loc_software_breakpoint
|
&& (bl->loc_type == bp_loc_software_breakpoint
|
||||||
&& solib_name_from_address (bl->pspace, bl->address))
|
&& (bl->shlib_disabled
|
||||||
|
|| solib_name_from_address (bl->pspace, bl->address)
|
||||||
|
|| userloaded_objfile_contains_address_p (bl->pspace,
|
||||||
|
bl->address))))
|
||||||
val = 0;
|
val = 0;
|
||||||
|
|
||||||
if (val)
|
if (val)
|
||||||
|
@ -7665,10 +7700,18 @@ disable_breakpoints_in_freed_objfile (struct objfile *objfile)
|
||||||
if (objfile == NULL)
|
if (objfile == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If the file is a shared library not loaded by the user then
|
/* OBJF_USERLOADED are dynamic modules manually managed by the user
|
||||||
solib_unloaded was notified and disable_breakpoints_in_unloaded_shlib
|
with add-symbol-file/remove-symbol-file. Similarly to how
|
||||||
was called. In that case there is no need to take action again. */
|
breakpoints in shared libraries are handled in response to
|
||||||
if ((objfile->flags & OBJF_SHARED) && !(objfile->flags & OBJF_USERLOADED))
|
"nosharedlibrary", mark breakpoints in OBJF_USERLOADED modules
|
||||||
|
shlib_disabled so they end up uninserted on the next global
|
||||||
|
location list update. Shared libraries not loaded by the user
|
||||||
|
aren't handled here -- they're already handled in
|
||||||
|
disable_breakpoints_in_unloaded_shlib, called by solib.c's
|
||||||
|
solib_unloaded observer. We skip objfiles that are not
|
||||||
|
OBJF_USERLOADED (nor OBJF_SHARED) as those aren't considered
|
||||||
|
dynamic objects (e.g. the main objfile). */
|
||||||
|
if ((objfile->flags & OBJF_USERLOADED) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ALL_BREAKPOINTS (b)
|
ALL_BREAKPOINTS (b)
|
||||||
|
@ -7700,7 +7743,11 @@ disable_breakpoints_in_freed_objfile (struct objfile *objfile)
|
||||||
if (is_addr_in_objfile (loc_addr, objfile))
|
if (is_addr_in_objfile (loc_addr, objfile))
|
||||||
{
|
{
|
||||||
loc->shlib_disabled = 1;
|
loc->shlib_disabled = 1;
|
||||||
loc->inserted = 0;
|
/* At this point, we don't know whether the object was
|
||||||
|
unmapped from the inferior or not, so leave the
|
||||||
|
inserted flag alone. We'll handle failure to
|
||||||
|
uninsert quietly, in case the object was indeed
|
||||||
|
unmapped. */
|
||||||
|
|
||||||
mark_breakpoint_location_modified (loc);
|
mark_breakpoint_location_modified (loc);
|
||||||
|
|
||||||
|
|
|
@ -89,3 +89,35 @@ memory_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
|
||||||
{
|
{
|
||||||
return gdbarch_memory_remove_breakpoint (gdbarch, bp_tgt);
|
return gdbarch_memory_remove_breakpoint (gdbarch, bp_tgt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
memory_validate_breakpoint (struct gdbarch *gdbarch,
|
||||||
|
struct bp_target_info *bp_tgt)
|
||||||
|
{
|
||||||
|
CORE_ADDR addr = bp_tgt->placed_address;
|
||||||
|
const gdb_byte *bp;
|
||||||
|
int val;
|
||||||
|
int bplen;
|
||||||
|
gdb_byte cur_contents[BREAKPOINT_MAX];
|
||||||
|
struct cleanup *cleanup;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Determine appropriate breakpoint contents and size for this
|
||||||
|
address. */
|
||||||
|
bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen);
|
||||||
|
|
||||||
|
if (bp == NULL || bp_tgt->placed_size != bplen)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Make sure we see the memory breakpoints. */
|
||||||
|
cleanup = make_show_memory_breakpoints_cleanup (1);
|
||||||
|
val = target_read_memory (addr, cur_contents, bplen);
|
||||||
|
|
||||||
|
/* If our breakpoint is no longer at the address, this means that
|
||||||
|
the program modified the code on us, so it is wrong to put back
|
||||||
|
the old value. */
|
||||||
|
ret = (val == 0 && memcmp (bp, cur_contents, bplen) == 0);
|
||||||
|
|
||||||
|
do_cleanups (cleanup);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -1453,6 +1453,22 @@ is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
userloaded_objfile_contains_address_p (struct program_space *pspace,
|
||||||
|
CORE_ADDR address)
|
||||||
|
{
|
||||||
|
struct objfile *objfile;
|
||||||
|
|
||||||
|
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||||
|
{
|
||||||
|
if ((objfile->flags & OBJF_USERLOADED) != 0
|
||||||
|
&& is_addr_in_objfile (address, objfile))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* The default implementation for the "iterate_over_objfiles_in_search_order"
|
/* The default implementation for the "iterate_over_objfiles_in_search_order"
|
||||||
gdbarch method. It is equivalent to use the ALL_OBJFILES macro,
|
gdbarch method. It is equivalent to use the ALL_OBJFILES macro,
|
||||||
searching the objfiles in the order they are stored internally,
|
searching the objfiles in the order they are stored internally,
|
||||||
|
|
|
@ -515,6 +515,13 @@ extern void objfiles_changed (void);
|
||||||
|
|
||||||
extern int is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile);
|
extern int is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile);
|
||||||
|
|
||||||
|
/* Return true if ADDRESS maps into one of the sections of the
|
||||||
|
userloaded ("add-symbol-file") objfiles of PSPACE and false
|
||||||
|
otherwise. */
|
||||||
|
|
||||||
|
extern int userloaded_objfile_contains_address_p (struct program_space *pspace,
|
||||||
|
CORE_ADDR address);
|
||||||
|
|
||||||
/* This operation deletes all objfile entries that represent solibs that
|
/* This operation deletes all objfile entries that represent solibs that
|
||||||
weren't explicitly loaded by the user, via e.g., the add-symbol-file
|
weren't explicitly loaded by the user, via e.g., the add-symbol-file
|
||||||
command. */
|
command. */
|
||||||
|
|
|
@ -2110,6 +2110,12 @@ extern int memory_remove_breakpoint (struct target_ops *, struct gdbarch *,
|
||||||
extern int memory_insert_breakpoint (struct target_ops *, struct gdbarch *,
|
extern int memory_insert_breakpoint (struct target_ops *, struct gdbarch *,
|
||||||
struct bp_target_info *);
|
struct bp_target_info *);
|
||||||
|
|
||||||
|
/* Check whether the memory at the breakpoint's placed address still
|
||||||
|
contains the expected breakpoint instruction. */
|
||||||
|
|
||||||
|
extern int memory_validate_breakpoint (struct gdbarch *gdbarch,
|
||||||
|
struct bp_target_info *bp_tgt);
|
||||||
|
|
||||||
extern int default_memory_remove_breakpoint (struct gdbarch *,
|
extern int default_memory_remove_breakpoint (struct gdbarch *,
|
||||||
struct bp_target_info *);
|
struct bp_target_info *);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
2014-04-23 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
|
* gdb.base/break-unload-file.c: New file.
|
||||||
|
* gdb.base/break-unload-file.exp: New file.
|
||||||
|
* gdb.base/sym-file-lib.c (baz): New function.
|
||||||
|
* gdb.base/sym-file-loader.c (struct segment) <mapped_size>: New
|
||||||
|
field.
|
||||||
|
(load): Store the segment's mapped size.
|
||||||
|
(unload): New function.
|
||||||
|
(unload_shlib): New function.
|
||||||
|
* gdb.base/sym-file-loader.h (unload_shlib): New declaration.
|
||||||
|
* gdb.base/sym-file-main.c (main): Unload, and reload the library,
|
||||||
|
set a breakpoint at baz, and call it.
|
||||||
|
* gdb.base/sym-file.exp: New tests for stale breakpoint
|
||||||
|
instructions.
|
||||||
|
|
||||||
2014-04-23 Pedro Alves <palves@redhat.com>
|
2014-04-23 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
* gdb.base/hbreak-in-shr-unsupported-shr.c: New file.
|
* gdb.base/hbreak-in-shr-unsupported-shr.c: New file.
|
||||||
|
|
35
gdb/testsuite/gdb.base/break-unload-file.c
Normal file
35
gdb/testsuite/gdb.base/break-unload-file.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2014 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/>. */
|
||||||
|
|
||||||
|
void
|
||||||
|
foo (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bar (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
foo ();
|
||||||
|
bar ();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
128
gdb/testsuite/gdb.base/break-unload-file.exp
Normal file
128
gdb/testsuite/gdb.base/break-unload-file.exp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
# Copyright 2014 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 that "file" doesn't leave stale breakpoints planted in the
|
||||||
|
# target.
|
||||||
|
|
||||||
|
standard_testfile
|
||||||
|
|
||||||
|
if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ![runto_main] then {
|
||||||
|
fail "Can't run to main"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run the test proper. ALWAYS_INSERT determines whether
|
||||||
|
# always-inserted mode is on/off, and BREAK_COMMAND is the break
|
||||||
|
# command being tested.
|
||||||
|
#
|
||||||
|
proc test_break { always_inserted break_command } {
|
||||||
|
global gdb_prompt binfile hex
|
||||||
|
|
||||||
|
with_test_prefix "always-inserted $always_inserted: $break_command" {
|
||||||
|
clean_restart $binfile
|
||||||
|
|
||||||
|
if ![runto_main] then {
|
||||||
|
fail "Can't run to main"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_breakpoints
|
||||||
|
|
||||||
|
gdb_test_no_output "set breakpoint always-inserted $always_inserted"
|
||||||
|
|
||||||
|
set test "$break_command foo"
|
||||||
|
gdb_test_multiple "$break_command foo" $test {
|
||||||
|
-re "No hardware breakpoint support in the target.*$gdb_prompt $" {
|
||||||
|
unsupported $test
|
||||||
|
return
|
||||||
|
}
|
||||||
|
-re "Hardware breakpoints used exceeds limit.*$gdb_prompt $" {
|
||||||
|
unsupported $test
|
||||||
|
return
|
||||||
|
}
|
||||||
|
-re "Cannot insert hardware breakpoint.*$gdb_prompt $" {
|
||||||
|
unsupported $test
|
||||||
|
return
|
||||||
|
}
|
||||||
|
-re ".*reakpoint .* at .*$gdb_prompt $" {
|
||||||
|
pass $test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# The breakpoint shouldn't be pending now.
|
||||||
|
gdb_test "info break" "y.*$hex.*in foo at.*" \
|
||||||
|
"breakpoint is not pending"
|
||||||
|
|
||||||
|
# Remove the file, while the breakpoint above is inserted in a
|
||||||
|
# function in the main objfile. GDB used to have a bug where
|
||||||
|
# it would mark the breakpoint as uninserted, but actually
|
||||||
|
# would leave it inserted in the target.
|
||||||
|
set test "file"
|
||||||
|
gdb_test_multiple "file" $test {
|
||||||
|
-re "Are you sure you want to change the file. .*y or n. $" {
|
||||||
|
send_gdb "y\n"
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "Discard symbol table from `.*'? .y or n. $" {
|
||||||
|
send_gdb "y\n"
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "No symbol file now\\.\r\n$gdb_prompt $" {
|
||||||
|
pass $test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test "info break" "y.*PENDING.*foo" \
|
||||||
|
"breakpoint is not pending"
|
||||||
|
|
||||||
|
# Now delete the breakpoint from GDB's tables, to make sure
|
||||||
|
# GDB doesn't reinsert it, masking the bug (with the bug, on
|
||||||
|
# re-insert, GDB would fill the shadow buffer with a
|
||||||
|
# breakpoint instruction). Avoid delete_breakpoints as that
|
||||||
|
# doesn't record a pass/fail.
|
||||||
|
gdb_test "delete" "" "delete all breakpoints" \
|
||||||
|
"Delete all breakpoints.*y or n.*$" "y"
|
||||||
|
|
||||||
|
# Re-add symbols back.
|
||||||
|
set test "file \$binfile"
|
||||||
|
gdb_test_multiple "file $binfile" $test {
|
||||||
|
-re "Are you sure you want to change the file. .*y or n. $" {
|
||||||
|
send_gdb "y\n"
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "Reading symbols from.*done.*$gdb_prompt $" {
|
||||||
|
pass $test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run to another function now. With the bug, GDB would trip
|
||||||
|
# on a spurious trap at foo.
|
||||||
|
gdb_test "b bar" ".*reakpoint .* at .*"
|
||||||
|
gdb_test "continue" "Breakpoint .*, bar .*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# While it doesn't trigger the original bug this is a regression test
|
||||||
|
# for, test with breakpoint always-inserted off for extra coverage.
|
||||||
|
foreach always_inserted { "off" "on" } {
|
||||||
|
test_break $always_inserted "break"
|
||||||
|
if {![skip_hw_breakpoint_tests]} {
|
||||||
|
test_break $always_inserted "hbreak"
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,3 +24,9 @@ foo (int a)
|
||||||
{
|
{
|
||||||
return a; /* gdb break at foo */
|
return a; /* gdb break at foo */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int
|
||||||
|
baz (int a)
|
||||||
|
{
|
||||||
|
return a; /* gdb break at baz */
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ sizeof ((hdr)->field) == sizeof (Elf_Addr) ? *(Elf_Addr *) (hdr)->field : \
|
||||||
struct segment
|
struct segment
|
||||||
{
|
{
|
||||||
uint8_t *mapped_addr;
|
uint8_t *mapped_addr;
|
||||||
|
size_t mapped_size;
|
||||||
Elf_External_Phdr *phdr;
|
Elf_External_Phdr *phdr;
|
||||||
struct segment *next;
|
struct segment *next;
|
||||||
};
|
};
|
||||||
|
@ -101,6 +102,7 @@ load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg)
|
||||||
{
|
{
|
||||||
struct segment *seg = NULL;
|
struct segment *seg = NULL;
|
||||||
uint8_t *mapped_addr = NULL;
|
uint8_t *mapped_addr = NULL;
|
||||||
|
size_t mapped_size = 0;
|
||||||
void *from = NULL;
|
void *from = NULL;
|
||||||
void *to = NULL;
|
void *to = NULL;
|
||||||
|
|
||||||
|
@ -110,6 +112,7 @@ load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg)
|
||||||
mapped_addr = (uint8_t *) mmap ((void *) GETADDR (phdr, p_vaddr),
|
mapped_addr = (uint8_t *) mmap ((void *) GETADDR (phdr, p_vaddr),
|
||||||
GET (phdr, p_memsz), perm,
|
GET (phdr, p_memsz), perm,
|
||||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
|
mapped_size = GET (phdr, p_memsz);
|
||||||
|
|
||||||
from = (void *) (addr + GET (phdr, p_offset));
|
from = (void *) (addr + GET (phdr, p_offset));
|
||||||
to = (void *) mapped_addr;
|
to = (void *) mapped_addr;
|
||||||
|
@ -122,6 +125,7 @@ load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
seg->mapped_addr = mapped_addr;
|
seg->mapped_addr = mapped_addr;
|
||||||
|
seg->mapped_size = mapped_size;
|
||||||
seg->phdr = phdr;
|
seg->phdr = phdr;
|
||||||
seg->next = 0;
|
seg->next = 0;
|
||||||
|
|
||||||
|
@ -173,6 +177,30 @@ get_origin (void)
|
||||||
return self_path;
|
return self_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unload/unmap a segment. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
unload (struct segment *seg)
|
||||||
|
{
|
||||||
|
munmap (seg->mapped_addr, seg->mapped_size);
|
||||||
|
free (seg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unload_shlib (struct library *lib)
|
||||||
|
{
|
||||||
|
struct segment *seg, *next_seg;
|
||||||
|
|
||||||
|
for (seg = lib->segments; seg != NULL; seg = next_seg)
|
||||||
|
{
|
||||||
|
next_seg = seg->next;
|
||||||
|
unload (seg);
|
||||||
|
}
|
||||||
|
|
||||||
|
close (lib->fd);
|
||||||
|
free (lib);
|
||||||
|
}
|
||||||
|
|
||||||
/* Mini shared library loader. No reallocation
|
/* Mini shared library loader. No reallocation
|
||||||
is performed for the sake of simplicity. */
|
is performed for the sake of simplicity. */
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,10 @@ struct library;
|
||||||
|
|
||||||
struct library *load_shlib (const char *file);
|
struct library *load_shlib (const char *file);
|
||||||
|
|
||||||
|
/* Unload a library. */
|
||||||
|
|
||||||
|
void unload_shlib (struct library *lib);
|
||||||
|
|
||||||
/* Lookup the address of FUNC. */
|
/* Lookup the address of FUNC. */
|
||||||
|
|
||||||
int lookup_function (struct library *lib, const char *func, void **addr);
|
int lookup_function (struct library *lib, const char *func, void **addr);
|
||||||
|
|
|
@ -42,6 +42,8 @@ main (int argc, const char *argv[])
|
||||||
char *text_addr = NULL;
|
char *text_addr = NULL;
|
||||||
int (*pbar) () = NULL;
|
int (*pbar) () = NULL;
|
||||||
int (*pfoo) (int) = NULL;
|
int (*pfoo) (int) = NULL;
|
||||||
|
int (*pbaz) () = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
lib = load_shlib (file);
|
lib = load_shlib (file);
|
||||||
if (lib == NULL)
|
if (lib == NULL)
|
||||||
|
@ -64,8 +66,28 @@ main (int argc, const char *argv[])
|
||||||
|
|
||||||
(*pfoo) (2);
|
(*pfoo) (2);
|
||||||
|
|
||||||
/* Notify GDB to remove the symbol file. */
|
/* Unload the library, invalidating all memory breakpoints. */
|
||||||
|
unload_shlib (lib);
|
||||||
|
|
||||||
|
/* Notify GDB to remove the symbol file. Also check that GDB
|
||||||
|
doesn't complain that it can't remove breakpoints from the
|
||||||
|
unmapped library. */
|
||||||
gdb_remove_symbol_file (text_addr);
|
gdb_remove_symbol_file (text_addr);
|
||||||
|
|
||||||
return 0;
|
/* Reload the library. */
|
||||||
|
lib = load_shlib (file); /* reload lib here */
|
||||||
|
if (lib == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (get_text_addr (lib, (void **) &text_addr) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
gdb_add_symbol_file (text_addr, file);
|
||||||
|
|
||||||
|
if (lookup_function (lib, "baz", (void *) &pbaz) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
(*pbaz) ();
|
||||||
|
|
||||||
|
return 0; /* end here */
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
# 11) 'info files' must not display ${lib_basename}, anymore.
|
# 11) 'info files' must not display ${lib_basename}, anymore.
|
||||||
# 12) Check that the breakpoints at foo and bar are pending.
|
# 12) Check that the breakpoints at foo and bar are pending.
|
||||||
# 13) Check that the execution can continue without error.
|
# 13) Check that the execution can continue without error.
|
||||||
|
# 14) Regression test for a stale breakpoints bug.
|
||||||
|
|
||||||
if {![is_elf_target]} {
|
if {![is_elf_target]} {
|
||||||
return 0
|
return 0
|
||||||
|
@ -159,4 +160,50 @@ gdb_test "info breakpoints 4" \
|
||||||
"breakpoint at bar is pending"
|
"breakpoint at bar is pending"
|
||||||
|
|
||||||
# 13) Check that the execution can continue without error.
|
# 13) Check that the execution can continue without error.
|
||||||
gdb_continue_to_end
|
set lnum_reload [gdb_get_line_number "reload lib here"]
|
||||||
|
gdb_breakpoint $lnum_reload
|
||||||
|
gdb_continue_to_breakpoint reload ".*${srcfile}:$lnum_reload.*"
|
||||||
|
|
||||||
|
# 14) Regression test for a stale breakpoints bug. Check whether
|
||||||
|
# unloading symbols manually without the program actually unloading
|
||||||
|
# the library, when breakpoints are inserted doesn't leave stale
|
||||||
|
# breakpoints behind.
|
||||||
|
with_test_prefix "stale bkpts" {
|
||||||
|
# Force breakpoints always inserted.
|
||||||
|
gdb_test_no_output "set breakpoint always-inserted on"
|
||||||
|
|
||||||
|
# Get past the library reload.
|
||||||
|
gdb_continue_to_breakpoint gdb_add_symbol_file
|
||||||
|
|
||||||
|
# Load the library's symbols.
|
||||||
|
gdb_test "add-symbol-file ${lib_syms} addr" \
|
||||||
|
"Reading symbols from .*${lib_syms}\\.\\.\\.done\\." \
|
||||||
|
"add-symbol-file ${lib_basename}.so addr" \
|
||||||
|
"add symbol table from file \".*${lib_syms}\"\
|
||||||
|
at.*\\(y or n\\) " \
|
||||||
|
"y"
|
||||||
|
|
||||||
|
# Set a breakpoint at baz, in the library.
|
||||||
|
gdb_breakpoint baz
|
||||||
|
|
||||||
|
gdb_test "info breakpoints 7" ".*y.*0x.*in baz.*" \
|
||||||
|
"breakpoint at baz is resolved"
|
||||||
|
|
||||||
|
# Unload symbols manually without the program actually unloading
|
||||||
|
# the library.
|
||||||
|
gdb_test "remove-symbol-file -a addr" \
|
||||||
|
"" \
|
||||||
|
"remove-symbol-file -a addr" \
|
||||||
|
"Remove symbol table from file \".*${lib_basename}\\.so\"\\?\
|
||||||
|
.*\\(y or n\\) " \
|
||||||
|
"y"
|
||||||
|
|
||||||
|
gdb_test "info breakpoints 7" ".*PENDING.*" \
|
||||||
|
"breakpoint at baz is pending"
|
||||||
|
|
||||||
|
# Check that execution can continue without error. If GDB leaves
|
||||||
|
# breakpoints behind, we'll get back a spurious SIGTRAP.
|
||||||
|
set lnum_end [gdb_get_line_number "end here"]
|
||||||
|
gdb_breakpoint $lnum_end
|
||||||
|
gdb_continue_to_breakpoint "end here" ".*end here.*"
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue