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