dld@JMW.LARCH.CS.CMU.EDU (David Detlefs) (08/15/89)
Michael et. al. -- Problem: G++ does not produce appropriate symbol table information for anonymous unions. Consider the following program: -------------------------------------------------- struct foo { union { int i; char* j; }; }; void main() { foo f; f.i = 7; f.j = "foofoo"; } -------------------------------------------------- G++ compiles this without complaint, and, as far as I can tell, correctly. However, if you compile it with -g, and run it under gdb, you can get the following script: b main Breakpoint 1 at 0xb5: file bug2.c, line 10. gdb% run Starting program: /usr0/dld/testing/bug2 Bpt 1, main (1, 2147481872, 2147481880) (bug2.c line 10) 10 f.i = 7; gdb% p f $1 = {} gdb% p f.i there is no field named i gdb% The problem is that when G++ generates symbol-table information for "struct foo" in dbxout.c, it decides that "foo" has no members, because it has no named members at the top-level. Below is a fix to dbxout.c that makes G++ produce symbol-table information that makes gdb treat members of anonymous unions as if they are top-level members of the classes in which they occur. That is, in the above case, you get gdb% p f $2 = {i = 0, j = 0x0} gdb% p f.i $3 = 0 gdb% Ideally, we would also like gdb to print out extra sets of curly braces to indicate which members of f are parts of the same anonymous union: gdb% p f $2 = {{i = 0, j = 0x0}} gdb% p f.i $3 = 0 gdb% ...But I can't figure out a way to do this. I suspect that it would require gdb modifications, since gdb seems to expect each field of a record to have a name. In the fix below, note that is important that the solution be recursive, so that cases such as -------------------------------------------------- struct bar { union { int i; union { float f; char* s; }; }; }; -------------------------------------------------- get treated correctly -- bar is considered to have three top-level members i, f, and s that all start at the same offset. Here is the fix to dbxout.c: ***After line 172, add: static void dbxout_field (); ***Change the else clause that begins at line 439 to else dbxout_field(tem, type); ***Add the following function: /* Output a field of a record type to the asm file. */ static void dbxout_field (field, type) register tree field, type; { if ( DECL_NAME (field) == 0 ) if ( TREE_CODE( TREE_TYPE (field) ) == UNION_TYPE ) { /* An anonymous union. Print out a field descriptor for each */ /* member of the union. */ tree tem2; for (tem2 = TYPE_FIELDS( TREE_TYPE (field) ); tem2; tem2 = TREE_CHAIN (tem2)) { dbxout_field(tem2, type); } return; } else /* Is a null field, only there to take up space. */ return; /* Otherwise, just output the description of the field. */ if (use_gdb_dbx_extensions || TREE_CODE (field) != CONST_DECL) { /* Continue the line if necessary, but not before the first field. */ if (field != TYPE_FIELDS (type)) CONTIN; fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (field))); CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (field))); if (use_gdb_dbx_extensions) { putc ('/', asmfile); #ifdef TREE_PRIVATE putc ((TREE_PRIVATE (field) ? '0' : TREE_PROTECTED (field) ? '1' : '2'), asmfile); #endif CHARS (2); } dbxout_type (TREE_TYPE (field), 0); if (TREE_CODE (field) == VAR_DECL) { if (use_gdb_dbx_extensions) { char *name = XSTR (XEXP (DECL_RTL (field), 0), 0); if (name[0] == '*') name += 1; /* Adding 1 here only works on systems which flush an initial underscore. */ fprintf (asmfile, ":%s;", name+1); CHARS (strlen (name)); } else { fprintf (asmfile, ",0,0;"); } } else if (TREE_CODE (field) == CONST_DECL) { /* Only get here if using GDB's extensions to DBX format. */ fprintf (asmfile, "~i%d;", TREE_INT_CST_LOW (DECL_INITIAL (field))); } else { fprintf (asmfile, ",%d,%d;", DECL_OFFSET (field), TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field)); } CHARS (23); } } Happier hacking. Dave