PR17431: following execs with "breakpoint always-inserted on"
authorPedro Alves <palves@redhat.com>
Thu, 2 Oct 2014 08:55:38 +0000 (09:55 +0100)
committerPedro Alves <palves@redhat.com>
Thu, 2 Oct 2014 09:05:46 +0000 (10:05 +0100)
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-10-02  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-10-02  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/ChangeLog
gdb/breakpoint.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/execl-update-breakpoints.c [new file with mode: 0644]
gdb/testsuite/gdb.base/execl-update-breakpoints.exp [new file with mode: 0644]

index 528f277a6e196bc1df6409d483ec2ca18b4c797d..18ebca44f87aa635821d54aee568a37ab780f163 100644 (file)
@@ -1,3 +1,9 @@
+2014-10-02  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.
+
 2014-10-02  Pedro Alves  <palves@redhat.com>
 
        * gdbthread.h (any_thread_of_process, any_live_thread_of_process):
index 44f6f1430700d7c4c6b2d8801d9959ba1c66526d..684f74c228fcc8fdbfe5963a8a85ba0e9ab08c65 100644 (file)
@@ -3839,11 +3839,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
index 4580eb23633fc506548d27cb67767345e000201a..87f3684e0efee6b6528d36371825d18387e1337d 100644 (file)
@@ -1,3 +1,9 @@
+2014-10-02  Pedro Alves  <palves@redhat.com>
+
+       PR breakpoints/17431
+       * gdb.base/execl-update-breakpoints.c: New file.
+       * gdb.base/execl-update-breakpoints.exp: New file.
+
 2014-10-01  Pedro Alves  <palves@redhat.com>
 
        * gdb.base/breakpoint-in-ro-region.c: New file.
diff --git a/gdb/testsuite/gdb.base/execl-update-breakpoints.c b/gdb/testsuite/gdb.base/execl-update-breakpoints.c
new file mode 100644 (file)
index 0000000..4eb76e8
--- /dev/null
@@ -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 (file)
index 0000000..eb1024b
--- /dev/null
@@ -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
+    }
+}