From patchwork Thu Sep 25 14:58:42 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 2970 Received: (qmail 5665 invoked by alias); 25 Sep 2014 14:58:54 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 5507 invoked by uid 89); 25 Sep 2014 14:58:53 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.3 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 25 Sep 2014 14:58:51 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s8PEwmbs025612 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Thu, 25 Sep 2014 10:58:49 -0400 Received: from brno.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s8PEwhD9006018 for ; Thu, 25 Sep 2014 10:58:47 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 2/3] PR17431: following execs with "breakpoint always-inserted on" Date: Thu, 25 Sep 2014 15:58:42 +0100 Message-Id: <1411657123-16228-3-git-send-email-palves@redhat.com> In-Reply-To: <1411657123-16228-1-git-send-email-palves@redhat.com> References: <1411657123-16228-1-git-send-email-palves@redhat.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 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 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 . */ + +#include +#include +#include +#include + +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 . + +# 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 + } +}