This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/3] PR17431: following execs with "breakpoint always-inserted on"


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]