This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 2/3] PR17431: following execs with "breakpoint always-inserted on"
- From: Pedro Alves <palves at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Thu, 25 Sep 2014 15:58:42 +0100
- Subject: [PATCH 2/3] PR17431: following execs with "breakpoint always-inserted on"
- Authentication-results: sourceware.org; auth=none
- References: <1411657123-16228-1-git-send-email-palves at redhat dot com>
Following an exec with "breakpoint always-inserted on" tries to insert
breakpoints in the new image at the addresses the symbols had in the
old image.
With "always-inserted off", we see:
gdb gdb.multi/multi-arch-exec -ex "set breakpoint always-inserted off"
GNU gdb (GDB) 7.8.50.20140924-cvs
...
(gdb) b main
Breakpoint 1 at 0x400664: file gdb.multi/multi-arch-exec.c, line 24.
^^^^^^^^
(gdb) c
The program is not being run.
(gdb) r
Starting program: testsuite/gdb.multi/multi-arch-exec
Breakpoint 1, main () at gdb/testsuite/gdb.multi/multi-arch-exec.c:24
24 execl (BASEDIR "/multi-arch-exec-hello",
(gdb) c
Continuing.
process 9212 is executing new program: gdb/testsuite/gdb.multi/multi-arch-exec-hello
Breakpoint 1, main () at gdb/testsuite/gdb.multi/hello.c:40
40 bar();
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x080484e4 in main at gdb/testsuite/gdb.multi/hello.c:40
^^^^^^^^^^
breakpoint already hit 2 times
(gdb)
Note how main was 0x400664 in multi-arch-exec, and 0x080484e4 in
gdb.multi/hello.
With "always-inserted on", we get:
Breakpoint 1, main () at gdb/testsuite/gdb.multi/multi-arch-exec.c:24
24 execl (BASEDIR "/multi-arch-exec-hello",
(gdb) c
Continuing.
infrun: target_wait (-1, status) =
infrun: 9444 [process 9444],
infrun: status->kind = execd
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_EXECD
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x400664
(gdb)
That is, GDB is trying to insert a breakpoint at 0x400664, after the
exec, and then that address happens to not be mapped at all in the new
image.
The problem is that update_breakpoints_after_exec is creating
breakpoints, which ends up in update_global_location_list immediately
inserting breakpoints if "breakpoints always-inserted" is "on".
update_breakpoints_after_exec is called very early when we see an exec
event. At that point, we haven't loaded the symbols of the new
post-exec image yet, and thus haven't reset breakpoint's addresses to
whatever they may be in the new image. All we should be doing in
update_breakpoints_after_exec is deleting breakpoints that no longer
make sense after an exec. So the fix removes those breakpoint
creations.
The question is then, if not here, where are those breakpoints
re-created? Turns out we don't need to do anything else, because at
the end of follow_exec, we call breakpoint_re_set, whose tail is also
creating exactly the same breakpoints update_breakpoints_after_exec is
currently creating:
breakpoint_re_set (void)
{
...
create_overlay_event_breakpoint ();
create_longjmp_master_breakpoint ();
create_std_terminate_master_breakpoint ();
create_exception_master_breakpoint ();
}
A new test is added to exercise this.
Tested on x86_64 Fedora 20.
gdb/
2014-09-25 Pedro Alves <palves@redhat.com>
PR breakpoints/17431
* breakpoint.c (update_breakpoints_after_exec): Don't create
overlay, longjmp, std terminate nor exception breakpoints here.
gdb/testsuite/
2014-09-25 Pedro Alves <palves@redhat.com>
PR breakpoints/17431
* gdb.base/execl-update-breakpoints.c: New file.
* gdb.base/execl-update-breakpoints.exp: New file.
---
gdb/breakpoint.c | 5 -
gdb/testsuite/gdb.base/execl-update-breakpoints.c | 38 +++++++
.../gdb.base/execl-update-breakpoints.exp | 114 +++++++++++++++++++++
3 files changed, 152 insertions(+), 5 deletions(-)
create mode 100644 gdb/testsuite/gdb.base/execl-update-breakpoints.c
create mode 100644 gdb/testsuite/gdb.base/execl-update-breakpoints.exp
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 3675b4f..c14bb49 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3824,11 +3824,6 @@ update_breakpoints_after_exec (void)
continue;
}
}
- /* FIXME what about longjmp breakpoints? Re-create them here? */
- create_overlay_event_breakpoint ();
- create_longjmp_master_breakpoint ();
- create_std_terminate_master_breakpoint ();
- create_exception_master_breakpoint ();
}
int
diff --git a/gdb/testsuite/gdb.base/execl-update-breakpoints.c b/gdb/testsuite/gdb.base/execl-update-breakpoints.c
new file mode 100644
index 0000000..4eb76e8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/execl-update-breakpoints.c
@@ -0,0 +1,38 @@
+/* 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/>. */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main (int argc, char **argv)
+{
+ size_t len;
+ char *bin;
+
+ len = strlen (argv[0]);
+ bin = malloc (len + 1);
+ memcpy (bin, argv[0], len + 1);
+ if (bin[len - 1] == '1')
+ bin[len - 1] = '2';
+
+ execl (bin, bin, (char *) NULL);
+ perror ("execl failed");
+ exit (1);
+}
diff --git a/gdb/testsuite/gdb.base/execl-update-breakpoints.exp b/gdb/testsuite/gdb.base/execl-update-breakpoints.exp
new file mode 100644
index 0000000..eb1024b
--- /dev/null
+++ b/gdb/testsuite/gdb.base/execl-update-breakpoints.exp
@@ -0,0 +1,114 @@
+# 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 when following an exec, we don't try to insert breakpoints
+# in the new image at the addresses the symbols had before the exec.
+
+# Remote protocol does not support follow-exec notifications.
+
+if [is_remote target] {
+ continue
+}
+
+standard_testfile
+
+# Build two copies of the program, each linked at a different address.
+# The address of "main" in the first binary should end up being an
+# unmapped address in the second binary.
+
+set exec1 ${testfile}1
+set exec2 ${testfile}2
+set binfile1 ${binfile}1
+set binfile2 ${binfile}2
+
+if { [prepare_for_testing "failed to prepare" ${exec1} "${srcfile}" \
+ [list debug ldflags=-Wl,-Ttext=0x1000000]] } {
+ return -1
+}
+if { [prepare_for_testing "failed to prepare" ${exec2} "${srcfile}" \
+ [list debug ldflags=-Wl,-Ttext=0x2000000]] } {
+ return -1
+}
+
+# First check whether the address of "main" in exec1 is readable in
+# exec2. If it is, then skip the test as unsupported.
+
+clean_restart ${exec1}
+if ![runto_main] then {
+ fail "Couldn't run to main"
+ return -1
+}
+
+set addr ""
+set test "main address first"
+gdb_test_multiple "p/x &main" $test {
+ -re " = (0x\[0-9a-f\]+)\r\n$gdb_prompt $" {
+ set addr $expect_out(1,string)
+ pass $test
+ }
+}
+
+clean_restart ${exec2}
+if ![runto_main] then {
+ fail "Couldn't run to main"
+ return -1
+}
+
+set cannot_access 0
+set test "probe memory access"
+gdb_test_multiple "x $addr" $test {
+ -re "Cannot access memory at address .*$gdb_prompt $" {
+ set cannot_access 1
+ pass $test
+ }
+ -re ".*$gdb_prompt $" {
+ pass $test
+ }
+}
+
+if {!$cannot_access} {
+ unsupported "main address is readable in second binary"
+ return
+}
+
+# The test proper. ALWAYS_INSERTED indicates whether testing in
+# "breakpoint always-inserted" mode.
+
+proc test { always_inserted } {
+ global exec1
+
+ clean_restart ${exec1}
+
+ gdb_test_no_output "set breakpoint always-inserted $always_inserted"
+
+ if ![runto_main] then {
+ fail "Couldn't run to main"
+ return -1
+ }
+
+ # On a buggy GDB, with always-inserted on, we'd see:
+ # (gdb) continue
+ # Continuing.
+ # Warning:
+ # Cannot insert breakpoint 1.
+ # Cannot access memory at address 0x10000ff
+ gdb_test "continue" "Breakpoint 1, main.*" "continue across exec"
+}
+
+foreach always_inserted { "off" "on" } {
+ with_test_prefix "always-inserted $always_inserted" {
+ test $always_inserted
+ }
+}
--
1.9.3