From 13fd3ff34329b882503c7c1ef44b3656d6ebb022 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Thu, 2 Oct 2014 09:55:38 +0100 Subject: [PATCH] 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-10-02 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-10-02 Pedro Alves PR breakpoints/17431 * gdb.base/execl-update-breakpoints.c: New file. * gdb.base/execl-update-breakpoints.exp: New file. --- gdb/ChangeLog | 6 + gdb/breakpoint.c | 5 - gdb/testsuite/ChangeLog | 6 + .../gdb.base/execl-update-breakpoints.c | 38 ++++++ .../gdb.base/execl-update-breakpoints.exp | 114 ++++++++++++++++++ 5 files changed, 164 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/ChangeLog b/gdb/ChangeLog index 528f277a6e1..18ebca44f87 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,9 @@ +2014-10-02 Pedro Alves + + 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 * gdbthread.h (any_thread_of_process, any_live_thread_of_process): diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 44f6f143070..684f74c228f 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -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 diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 4580eb23633..87f3684e0ef 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2014-10-02 Pedro Alves + + PR breakpoints/17431 + * gdb.base/execl-update-breakpoints.c: New file. + * gdb.base/execl-update-breakpoints.exp: New file. + 2014-10-01 Pedro Alves * 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 index 00000000000..4eb76e8f5cb --- /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 00000000000..eb1024b1f8a --- /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 + } +} -- 2.30.2