--- /dev/null 2002-07-23 14:50:16.000000000 -0400 +++ gdb-5.2.1/gdb/testsuite/gdb.c++/hang1.C 2002-08-21 18:49:50.000000000 -0400 @@ -0,0 +1,3 @@ +#include "hang.H" + +int main (int argc, char **argv) { return 0; } --- /dev/null 2002-07-23 14:50:16.000000000 -0400 +++ gdb-5.2.1/gdb/testsuite/gdb.c++/hang2.C 2002-08-21 18:49:50.000000000 -0400 @@ -0,0 +1,8 @@ +#include "hang.H" + +struct B +{ + int member_of_B; +}; + +int var_in_b = 1729; --- /dev/null 2002-07-23 14:50:16.000000000 -0400 +++ gdb-5.2.1/gdb/testsuite/gdb.c++/hang3.C 2002-08-21 18:49:50.000000000 -0400 @@ -0,0 +1,4 @@ +#include "hang.H" + +const struct B *const_B_ptr; +int var_in_hang3 = 42; --- /dev/null 2002-07-23 14:50:16.000000000 -0400 +++ gdb-5.2.1/gdb/testsuite/gdb.c++/hang.exp 2002-08-21 18:49:50.000000000 -0400 @@ -0,0 +1,128 @@ +# Copyright (C) 2002 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@prep.ai.mit.edu + +if $tracelevel then { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +if { [skip_cplus_tests] } { continue } + +set testfile hang +set binfile ${objdir}/${subdir}/${testfile} + +foreach file {hang1 hang2 hang3} { + if {[gdb_compile "${srcdir}/${subdir}/${file}.C" "${file}.o" object {c++ debug}] != ""} { + gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." + } +} + +if {[gdb_compile "hang1.o hang2.o hang3.o" ${binfile} executable {c++ debug}] != "" } { + gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." +} + + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + + +# As of May 1, 2002, GDB hangs trying to read the debug info for the +# `hang2.o' compilation unit from the executable `hang', when compiled +# by g++ 2.96 with STABS debugging info. Here's what's going on, as +# best as I can tell. +# +# The definition of `struct A' in `hang.H' refers to `struct B' as an +# incomplete type. The stabs declare type number (1,3) to be a cross- +# reference type, `xsB:'. +# +# The definition of `struct C' contains a nested definition for +# `struct B' --- or more properly, `struct C::B'. However, the stabs +# fail to qualify the structure tag: it just looks like a definition +# for `struct B'. I think this is a compiler bug, but perhaps GCC +# doesn't emit qualified names for a reason. +# +# `hang.H' gets #included by both `hang1.C' and `hang2.C'. So the +# stabs for `struct A', the incomplete `struct B', and `struct C' +# appear in both hang1.o's and hang2.o's stabs. +# +# When those two files are linked together, since hang2.o appears +# later in the command line, its #inclusion of `hang.H' gets replaced +# with an N_EXCL stab, referring back to hang1.o's stabs for the +# header file. +# +# When GDB builds psymtabs for the executable hang, it notes that +# hang2.o's stabs contain an N_EXCL referring to a header that appears +# in full in hang1.o's stabs. So hang2.o's psymtab lists a dependency +# on hang1.o's psymtab. +# +# When the user types the command `print var_in_b', GDB scans the +# psymtabs for a symbol by that name, and decides to read full symbols +# for `hang2.o'. +# +# Since `hang2.o''s psymtab lists `hang1.o' as a dependency, GDB first +# reads `hang1.o''s symbols. When GDB sees `(1,3)=xsB:', it creates a +# type object for `struct B', sets its TYPE_FLAG_STUB flag, and +# records it as type number `(1,3)'. +# +# When GDB finds the definition of `struct C::B', since the stabs +# don't indicate that the type is nested within C, it treats it as +# a definition of `struct B'. +# +# When GDB is finished reading `hang1.o''s symbols, it calls +# `cleanup_undefined_types'. This function mistakes the definition of +# `struct C::B' for a definition for `struct B', and overwrites the +# incomplete type object for the real `struct B', using `memcpy'. Now +# stabs type number `(1,3)' refers to this (incorrect) complete type. +# Furthermore, the `memcpy' simply copies the original's `cv_type' +# field to the target, giving the target a corrupt `cv_type' ring: the +# chain does not point back to the target type. +# +# Having satisfied `hang2.o''s psymtab's dependencies, GDB begins to +# read `hang2.o''s symbols. These contain the true definition for +# `struct B', which refers to type number `(1,3)' as the type it's +# defining. GDB looks up type `(1,3)', and finds the (incorrect) +# complete type established by the call to `cleanup_undefined_types' +# above. However, it doesn't notice that the type is already defined, +# and passes it to `read_struct_type', which then writes the new +# definition's size, field list, etc. into the type object which +# already has those fields initialized. Adding insult to injury, +# `read_struct_type' then calls `finish_cv_type'; since the `memcpy' +# in `cleanup_undefined_types' corrupted the target type's `cv_type' +# ring, `finish_cv_type' enters an infinite loop. + +# This checks that GDB recognizes when a structure is about to be +# overwritten, and refuses, with a complaint. +gdb_test "print var_in_b" " = 1729" "doesn't overwrite struct type" + +# This checks that cleanup_undefined_types doesn't create corrupt +# cv_type chains. Note that var_in_hang3 does need to be declared in +# a separate compilation unit, whose psymtab depends on hang1.o's +# psymtab. Otherwise, GDB won't call cleanup_undefined_types (as it +# finishes hang1.o's symbols) before it calls make_cv_type (while +# reading hang3.o's symbols). +# +# The bug only happens when you compile with -gstabs+; Otherwise, GCC +# won't include the `const' qualifier on `const_B_ptr' in `hang3.o''s +# STABS, so GDB won't try to create a const variant of the smashed +# struct type, and get caught by the corrupted cv_type chain. +gdb_test "print var_in_hang3" " = 42" "doesn't corrupt cv_type chain" --- /dev/null 2002-07-23 14:50:16.000000000 -0400 +++ gdb-5.2.1/gdb/testsuite/gdb.c++/hang.H 2002-08-21 18:49:50.000000000 -0400 @@ -0,0 +1,12 @@ +struct A +{ + struct B *b_ptr_in_a; +}; + +struct C +{ + struct B + { + int member_of_B_in_C; + }; +}; --- gdb-5.2.1/gdb/testsuite/ChangeLog.misc 2002-07-03 02:00:28.000000000 -0400 +++ gdb-5.2.1/gdb/testsuite/ChangeLog 2002-08-21 18:49:50.000000000 -0400 @@ -0,0 +1,11 @@ +2002-05-06 Jim Blandy + + * gdb.c++/hang.exp: Check for corruption of the cv_type chain. + * gdb.c++/hang3.C: New file. + +2002-05-02 Jim Blandy + + * gdb.c++/hang1.C, gdb.c++/hang2.C, gdb.c++/hang.H, + gdb.c++/hang.exp: New test. + + --- gdb-5.2.1/gdb/gdbtypes.c.misc 2002-02-08 12:34:33.000000000 -0500 +++ gdb-5.2.1/gdb/gdbtypes.c 2002-08-21 18:49:50.000000000 -0400 @@ -521,10 +521,10 @@ /* Replace the contents of ntype with the type *type. - This function should not be necessary, but is due to quirks in the stabs - reader. This should go away. It does not handle the replacement type - being cv-qualified; it could be easily fixed to, but it should go away, - remember? */ + When building recursive types, it is necessary to update a type's + definition after people already have references to it. The C + language's concept of an `incomplete type' is an acknowledgement of + this. */ void replace_type (struct type *ntype, struct type *type) { --- gdb-5.2.1/gdb/inferior.h.misc 2001-11-21 19:23:12.000000000 -0500 +++ gdb-5.2.1/gdb/inferior.h 2002-08-21 18:49:50.000000000 -0400 @@ -83,7 +83,11 @@ pointer needed for later doing the cleanup. */ extern struct cleanup * save_inferior_ptid (void); -extern void set_sigint_trap (void); +/* Establish a SIGINT signal handler which will pass SIGINT to the + inferior. A non-zero value PID designates the process to deliver + the signal to. Otherwise, the signal will be passed to the process + designated by inferior_ptid. */ +extern void set_sigint_trap (int pid); extern void clear_sigint_trap (void); --- gdb-5.2.1/gdb/inflow.c.misc 2001-07-23 15:21:51.000000000 -0400 +++ gdb-5.2.1/gdb/inflow.c 2002-08-21 18:49:50.000000000 -0400 @@ -598,6 +598,10 @@ } } +/* PID to signal when passing SIGINT. Set to 0 to use pid associated + with inferior_ptid. */ +static int sigint_pid; + /* Call set_sigint_trap when you need to pass a signal on to an attached process when handling SIGINT */ @@ -606,18 +610,27 @@ pass_signal (int signo) { #ifndef _WIN32 - kill (PIDGET (inferior_ptid), SIGINT); + int pid; + + if (sigint_pid) + pid = sigint_pid; + else + pid = PIDGET (inferior_ptid); + + kill (pid, SIGINT); #endif } +/* Original SIGINT signal handler. */ static void (*osig) (); void -set_sigint_trap (void) +set_sigint_trap (int pid) { if (attach_flag || inferior_thisrun_terminal) { osig = (void (*)()) signal (SIGINT, pass_signal); + sigint_pid = pid; } } @@ -627,6 +640,7 @@ if (attach_flag || inferior_thisrun_terminal) { signal (SIGINT, osig); + sigint_pid = 0; } } --- gdb-5.2.1/gdb/lin-lwp.c.misc 2002-02-24 16:53:02.000000000 -0500 +++ gdb-5.2.1/gdb/lin-lwp.c 2002-08-21 18:49:50.000000000 -0400 @@ -120,6 +120,16 @@ #define is_lwp(ptid) (GET_LWP (ptid) != 0) #define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0) +/* An alternate pid to use for passing signals to the inferior. Normally, + signals are just passed to PIDGET (inferior_ptid), but it's not + desirable to do this when the user has attached to a cloned + process. In that case, we set ``signal_pid'' to the pid of a + non-cloned process. Note, however, that a zero-valued + ``signal_pid'' indicates that the normal mechanism should be used. + Note: This pid should also probably be used for reading/writing + memory. */ +int signal_pid; + /* If the last reported event was a SIGTRAP, this variable is set to the process id of the LWP/thread that got it. */ ptid_t trap_ptid; @@ -341,6 +351,49 @@ } #endif +/* Warn user if overall process id is a clone and do some other + housekeeping if this is the case. PTID repesents a non-cloned + process found during the process of attaching to LWPs other than + the overall process id. */ + +static void +set_up_clone_as_overall_process_id (ptid_t ptid) +{ + struct lwp_info *lp; + struct lwp_info *olp; + + gdb_assert (GET_PID (ptid) != GET_LWP (ptid)); + + lp = find_lwp_pid (ptid); + gdb_assert (lp != NULL); + gdb_assert (!lp->cloned); + + /* Find the LWP associated with the overall PID. */ + olp = find_lwp_pid (BUILD_LWP (GET_PID (ptid), GET_PID (ptid))); + gdb_assert (olp != NULL); + + if (!olp->cloned) + { + /* Overall process id isn't a clone, so bail out. Note: This + probably represents an error though since there are two + cloned processes! So we'll warn about it... */ + + warning ("LWP %d and LWP %ld are both non-cloned processes.", + GET_PID (ptid), GET_LWP (ptid)); + } + else + { + /* Overall process id is a clone, and PTID isn't. This + means that the user probably should've attached to + GET_LWP (PTID) instead. Issue a warning. */ + + warning ("Attached to a cloned process. You may wish to detach " + "and attach to LWP %ld instead.", GET_LWP (ptid)); + signal_pid = GET_LWP (ptid); + } + +} + /* Attach to the LWP specified by PID. If VERBOSE is non-zero, print a message telling the user that a new LWP has been added to the process. */ @@ -390,16 +443,9 @@ && WIFSTOPPED (status) && WSTOPSIG (status)); lp->stopped = 1; - } - else - { - /* We assume that the LWP representing the original process - is already stopped. Mark it as stopped in the data structure - that the lin-lwp layer uses to keep track of threads. Note - that this won't have already been done since the main thread - will have, we assume, been stopped by an attach from a - different layer. */ - lp->stopped = 1; + + if (!lp->cloned) + set_up_clone_as_overall_process_id (ptid); } } @@ -488,6 +534,7 @@ gdb_assert (num_lwps == 1); trap_ptid = null_ptid; + signal_pid = 0; /* Destroy LWP info; it's no longer valid. */ init_lwp_list (); @@ -953,17 +1000,26 @@ int save_errno; int status; pid_t pid; + int cloned; + struct lwp_info *lp; + ptid_t lwp_id; do { - set_sigint_trap (); /* Causes SIGINT to be passed on to the + set_sigint_trap (0); /* Causes SIGINT to be passed on to the attached process. */ set_sigio_trap (); pid = waitpid (GET_PID (ptid), &status, 0); if (pid == -1 && errno == ECHILD) - /* Try again with __WCLONE to check cloned processes. */ - pid = waitpid (GET_PID (ptid), &status, __WCLONE); + { + /* Try again with __WCLONE to check cloned processes. */ + pid = waitpid (GET_PID (ptid), &status, __WCLONE); + cloned = 1; + } + else + cloned = 0; + save_errno = errno; clear_sigio_trap (); @@ -981,6 +1037,16 @@ return minus_one_ptid; } + /* Mark the lwp as stopped. */ + lwp_id = BUILD_LWP (pid, pid); + lp = find_lwp_pid (lwp_id); + if (lp == NULL) + { + lp = add_lwp (lwp_id); + lp->cloned = cloned; + } + lp->stopped = 1; + store_waitstatus (ourstatus, status); return pid_to_ptid (pid); } @@ -1027,7 +1093,7 @@ status_to_str (status), GET_LWP (lp->ptid)); } - /* But if we don't fine one, we'll have to wait, and check both + /* But if we don't find one, we'll have to wait, and check both cloned and uncloned processes. We start with the cloned processes. */ options = __WCLONE | WNOHANG; @@ -1079,8 +1145,8 @@ stop_wait_callback (lp, NULL); } - set_sigint_trap (); /* Causes SIGINT to be passed on to the - attached process. */ + set_sigint_trap (signal_pid); /* Causes SIGINT to be passed on to the + attached process. */ set_sigio_trap (); while (status == 0) @@ -1334,6 +1400,7 @@ lin_lwp_mourn_inferior (void) { trap_ptid = null_ptid; + signal_pid = 0; /* Destroy LWP info; it's no longer valid. */ init_lwp_list (); --- gdb-5.2.1/gdb/linux-proc.c.misc 2002-02-24 17:31:19.000000000 -0500 +++ gdb-5.2.1/gdb/linux-proc.c 2002-08-21 18:49:50.000000000 -0400 @@ -77,7 +77,14 @@ if (ret > 0 && ret != EOF && *inode != 0) { - ret += fscanf (mapfile, "%s\n", filename); + /* Eat everything up to EOL for the filename. This will prevent + weird filenames (such as one with embedded whitespace) from + confusing this code. It also makes this code more robust + in respect to annotations the kernel may add after the + filename. + + Note the filename is used for informational purposes only. */ + ret += fscanf (mapfile, "%[^\n]\n", filename); } else { --- gdb-5.2.1/gdb/stabsread.c.misc 2002-02-20 13:40:52.000000000 -0500 +++ gdb-5.2.1/gdb/stabsread.c 2002-08-21 18:49:50.000000000 -0400 @@ -136,6 +136,7 @@ attach_fields_to_type (struct field_info *, struct type *, struct objfile *); static struct type *read_struct_type (char **, struct type *, + enum type_code, struct objfile *); static struct type *read_array_type (char **, struct type *, @@ -235,6 +236,9 @@ static struct complaint unresolved_sym_chain_complaint = {"%s: common block `%s' from global_sym_chain unresolved", 0, 0}; +static struct complaint multiply_defined_struct = +{"struct/union type gets multiply defined: %s%s", 0, 0}; + static struct complaint stabs_general_complaint = {"%s", 0, 0}; @@ -2536,7 +2540,24 @@ the related problems with unnecessarily stubbed types; someone motivated should attempt to clean up the issue here as well. Once a type pointed to has been created it - should not be modified. */ + should not be modified. + + Well, it's not *absolutely* wrong. Constructing recursive + types (trees, linked lists) necessarily entails modifying + types after creating them. Constructing any loop structure + entails side effects. The Dwarf 2 reader does handle this + more gracefully (it never constructs more than once + instance of a type object, so it doesn't have to copy type + objects wholesale), but it still mutates type objects after + other folks have references to them. + + Keep in mind that this circularity/mutation issue shows up + at the source language level, too: C's "incomplete types", + for example. So the proper cleanup, I think, would be to + limit GDB's type smashing to match exactly those required + by the source language. So GDB could have a + "complete_this_type" function, but never create unnecessary + copies of a type otherwise. */ replace_type (type, xtype); TYPE_NAME (type) = NULL; TYPE_TAG_NAME (type) = NULL; @@ -2801,18 +2822,21 @@ case 's': /* Struct type */ case 'u': /* Union type */ - type = dbx_alloc_type (typenums, objfile); - switch (type_descriptor) - { - case 's': - TYPE_CODE (type) = TYPE_CODE_STRUCT; - break; - case 'u': - TYPE_CODE (type) = TYPE_CODE_UNION; - break; - } - type = read_struct_type (pp, type, objfile); - break; + { + enum type_code type_code = TYPE_CODE_UNDEF; + type = dbx_alloc_type (typenums, objfile); + switch (type_descriptor) + { + case 's': + type_code = TYPE_CODE_STRUCT; + break; + case 'u': + type_code = TYPE_CODE_UNION; + break; + } + type = read_struct_type (pp, type, type_code, objfile); + break; + } case 'a': /* Array type */ if (**pp != 'r') @@ -4155,6 +4179,46 @@ return 1; } + +/* Sometimes GCC emits struct type forward references without the + necessary qualification. If there's more than one definition for + different types that share the same unqualified name, GDB will + think they're both definitions of the same type, and try to wipe + out each earlier definition with it successor. + + Complain that this is happening to TYPE. */ +static void +complain_about_struct_wipeout (struct type *type) +{ + char *name = ""; + char *kind = ""; + + if (TYPE_TAG_NAME (type)) + { + name = TYPE_TAG_NAME (type); + switch (TYPE_CODE (type)) + { + case TYPE_CODE_STRUCT: kind = "struct "; break; + case TYPE_CODE_UNION: kind = "union "; break; + case TYPE_CODE_ENUM: kind = "enum "; break; + default: kind = ""; + } + } + else if (TYPE_NAME (type)) + { + name = TYPE_NAME (type); + kind = ""; + } + else + { + name = ""; + kind = ""; + } + + complain (&multiply_defined_struct, kind, name); +} + + /* Read the description of a structure (or union type) and return an object describing the type. @@ -4170,7 +4234,8 @@ */ static struct type * -read_struct_type (char **pp, struct type *type, struct objfile *objfile) +read_struct_type (char **pp, struct type *type, enum type_code type_code, + struct objfile *objfile) { struct cleanup *back_to; struct field_info fi; @@ -4178,9 +4243,24 @@ fi.list = NULL; fi.fnlist = NULL; + /* Sometimes GCC emits struct type forward references without the + necessary qualification. If there's more than one definition for + different types that share the same unqualified name, GDB will + think they're both definitions of the same type, and try to wipe + out each earlier definition with it successor. */ + if (! (TYPE_CODE (type) == TYPE_CODE_UNDEF + || TYPE_STUB (type))) + { + complain_about_struct_wipeout (type); + + /* It's probably best to return the type unchanged. */ + return type; + } + back_to = make_cleanup (null_cleanup, 0); INIT_CPLUS_SPECIFIC (type); + TYPE_CODE (type) = type_code; TYPE_FLAGS (type) &= ~TYPE_FLAG_STUB; /* First comes the total size in bytes. */ @@ -5052,10 +5132,7 @@ && (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE (*type)) && STREQ (SYMBOL_NAME (sym), typename)) - { - memcpy (*type, SYMBOL_TYPE (sym), - sizeof (struct type)); - } + replace_type (*type, SYMBOL_TYPE (sym)); } } } --- gdb-5.2.1/gdb/symtab.c.misc 2002-02-27 13:22:24.000000000 -0500 +++ gdb-5.2.1/gdb/symtab.c 2002-08-21 18:49:50.000000000 -0400 @@ -590,7 +590,11 @@ /* If we are using C++ language, demangle the name before doing a lookup, so we can always binary search. */ - if (current_language->la_language == language_cplus) + /* NOTE: We need to always try to demangle since the current_language might + be something other than C++ at the point when we are trying to set a + breakpoint in C++ code. This problem needs fixing in a more elegant way + in the mainline gdb sources. -fnf */ + if (1 || (current_language->la_language == language_cplus)) { modified_name2 = cplus_demangle (modified_name, DMGL_ANSI | DMGL_PARAMS); if (modified_name2) --- gdb-5.2.1/gdb/ChangeLog.misc 2002-08-21 18:51:01.000000000 -0400 +++ gdb-5.2.1/gdb/ChangeLog 2002-08-21 18:51:23.000000000 -0400 @@ -0,0 +1,52 @@ +2002-05-06 Jim Blandy + + * stabsread.c (cleanup_undefined_types): Use replace_type, not memcpy. + (read_type): Doc fix. + * gdbtypes.c (replace_type): Doc fix. + +2002-05-02 Jim Blandy + + * stabsread.c (multiply_defined_struct): New complaint. + (read_struct_type): If the type we were passed isn't empty, or + incomplete, don't read the new struct type into it; complain, + and return the original type unchanged. Take a new `type_code' + argument, which is the type code for the new type. + (read_type): Rather than storing the type's type code here, pass + it as an argument to read_struct_type, and let that take care of + storing it. That way, we don't overwrite the original type code, + so read_struct_type can use it to decide whether we're overwriting + something we shouldn't. + (complain_about_struct_wipeout): New function. + +2002-03-22 Jim Blandy + + Patch for the `gcore' command from Jeff Law: + * linux-proc.c (read_mapping): Do not get confused by embedded + whitespace or kernel annotations after the filename when reading + /proc//maps. + + C++ fix from Fred Fish: + * symtab.c (lookup_symbol): Always try to demangle the name to + lookup, regardless of the current language. + + Thread interruption fix from Kevin Buettner: + * inferior.h (set_sigint_trap): Add new parameter representing + the pid to send signal to. + * inflow.c (set_sigint_trap): Likewise. Adjust all callers. + Affected files are: inftarg.c, lin-lwp.c, lynx-nat.c, and + symn-nat.c. + (sigint_pid): New static global variable. + (pass_signal): Use ``sigint_pid'' to determine process to send + signal to. + (clear_sigint_trap): Clear ``sigint_pid''. + * lin-lwp.c (signal_pid): New static global. + (set_up_clone_as_overall_process_id): New function. + (lin_lwp_attach_lwp): Remove code which handles the overall + process being stopped in another layer. Detect existence of + a non-cloned process as a subsiderary LWP. + (lin_lwp_detach, lin_lwp_mourn_inferior): Clear signal_pid. + (child_wait): Mark LWP as stopped. Also set flag indicating + whether LWP is a cloned process or not. + (lin_lwp_wait): Pass ``signal_pid'' to set_sigint_trap(). + +