aboutsummaryrefslogtreecommitdiff
path: root/gcc-1.40/symout.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-1.40/symout.c')
-rw-r--r--gcc-1.40/symout.c1267
1 files changed, 1267 insertions, 0 deletions
diff --git a/gcc-1.40/symout.c b/gcc-1.40/symout.c
new file mode 100644
index 0000000..e6ed44a
--- /dev/null
+++ b/gcc-1.40/symout.c
@@ -0,0 +1,1267 @@
+/* Output GDB-format symbol table information from GNU compiler.
+ Copyright (C) 1987, 1988 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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 1, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include "symseg.h"
+#include "rtl.h"
+#include "gdbfiles.h"
+#include <stdio.h>
+#undef NULL
+/* <...> used here so one can prevent use of ./stddef.h
+ by changing the -I options used. */
+#include <stddef.h>
+
+/* Get N_SO from stab.h if we can expect the file to exist. */
+#ifdef DBX_DEBUGGING_INFO
+#ifdef USG
+#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */
+#else
+#include <stab.h> /* On BSD, use the system's stab.h. */
+#endif /* not USG */
+#endif
+
+/* .stabs code for source file name. */
+#ifndef N_SO
+#define N_SO 0x64
+#endif
+
+/* Unix maximum on file name length. Needed for getwd. */
+#define MAXNAMLEN 1024
+
+/* Get the number to output for a reference to type TYPE. */
+#define TYPE_OUTPUT_ADDRESS(TYPE) \
+ TYPE_SYMTAB_ADDRESS (TYPE_MAIN_VARIANT (TYPE))
+
+/* Stream for writing symbol table file. */
+static FILE *symfile;
+
+/* Name of symbol table file. */
+static char *symfile_name;
+
+/* Stream for writing to assembler file. */
+static FILE *asmfile;
+
+/* Address for allocating space in symbol table file.
+ Changes in this variable are paired globally with writes to symfile,
+ but often we allocate many structures, advancing next_address,
+ before writing any of them. */
+static int next_address;
+
+/* Chain recording all the types that have been output,
+ giving the address-in-the-symseg of each one. */
+
+struct typevec_elt
+{
+ int address;
+ struct typevec_elt *next;
+};
+
+static struct typevec_elt *typevec;
+
+/* Number of types recorded so far in the chain. */
+
+static int total_types;
+
+/* Lists of types to which forward references have been made.
+ Separate lists for temporary and permanent types. */
+
+static tree temporary_fwd_refs;
+static tree permanent_fwd_refs;
+
+/* `blockvec' is a chain recording all the symbol-blocks that have been output,
+ giving the address-in-the-symseg of each one. */
+
+struct blockvec_elt
+{
+ int address;
+ struct blockvec_elt *next;
+};
+
+static struct blockvec_elt *blockvec;
+
+/* Number of blocks recorded so far in the chain. */
+
+static int total_blocks;
+
+static void symout_range_bounds ();
+static void symout_array_domain ();
+static void symout_record_fields ();
+static void symout_enum_values ();
+static void symout_record_field_names ();
+static void symout_enum_value_names ();
+static int subrange_p ();
+static void symout_strings_skip ();
+static void symout_strings_print ();
+
+/* At the beginning of compilation, start writing the symbol table.
+ Initialize the type and block chain.
+ Also open and initialize the symseg file. */
+
+void
+symout_init (filename, asm_file, sourcename)
+ char *filename;
+ FILE *asm_file;
+ char *sourcename;
+{
+ struct symbol_root buffer;
+
+#ifdef VMS
+ fatal ("Cannot write GDB debugging format on VMS");
+#endif
+
+ asmfile = asm_file;
+ fprintf (asmfile, ".text 0\n.gdbbeg 0\n.gdbbeg 1\n");
+ fprintf (asmfile,
+ "Ltext:\t.stabs \"%s\",%d,0,0,Ltext\n",
+ sourcename, N_SO);
+ fprintf (asmfile, ".data 0\nLdata:\n");
+ ASM_OUTPUT_LOCAL (asmfile, "Lbss", 0, 0);
+ fprintf (asmfile, ".gdbsym Ldata,%d\n",
+ (char *) &buffer.databeg - (char *) &buffer);
+ fprintf (asmfile, ".gdbsym Lbss,%d\n",
+ (char *) &buffer.bssbeg - (char *) &buffer);
+
+ symfile = fopen (filename, "w");
+ if (symfile == 0)
+ pfatal_with_name (filename);
+ symfile_name = (char *) malloc (strlen (filename) + 1);
+ strcpy (symfile_name, filename);
+
+ typevec = 0;
+ blockvec = 0;
+ total_types = 0;
+ total_blocks = 0;
+
+ permanent_fwd_refs = 0;
+ temporary_fwd_refs = 0;
+
+ bzero (&buffer, sizeof buffer);
+ fwrite (&buffer, sizeof buffer, 1, symfile);
+
+ next_address = sizeof buffer;
+}
+
+/* Functions for outputting strings into the symbol table.
+ The string to be output is effectively the concatenation of
+ the two strings P1 and P2. Their lengths are given as S1 and S2.
+ If P1 or P2 is zero, that string is not used.
+
+ A null character is output to terminate the string,
+ and it is followed by more nulls as padding to a word boundary. */
+
+static void
+symout_strings (p1, s1, p2, s2)
+ char *p1;
+ int s1;
+ char *p2;
+ int s2;
+{
+ symout_strings_print (p1, s1, p2, s2);
+ symout_strings_skip (p1, s1, p2, s2);
+}
+
+/* Like symout_strings but only output; do not update next_address. */
+
+static void
+symout_strings_print (p1, s1, p2, s2)
+ char *p1;
+ int s1;
+ char *p2;
+ int s2;
+{
+ register int total;
+
+ if (p1 && s1 == 0)
+ s1 = strlen (p1);
+ if (p2 && s2 == 0)
+ s2 = strlen (p2);
+
+ if (p1)
+ fwrite (p1, s1, 1, symfile);
+ if (p2)
+ fwrite (p2, s2, 1, symfile);
+ putc (0, symfile);
+
+ total = s1 + s2 + 1;
+ while (total % sizeof (int))
+ {
+ putc (0, symfile);
+ total++;
+ }
+}
+
+/* Like symout_strings but just update next_address; do not output. */
+
+static void
+symout_strings_skip (p1, s1, p2, s2)
+ char *p1;
+ int s1;
+ char *p2;
+ int s2;
+{
+ register int total;
+
+ if (p1 && s1 == 0)
+ s1 = strlen (p1);
+ if (p2 && s2 == 0)
+ s2 = strlen (p2);
+
+ total = s1 + s2 + 1;
+ while (total % sizeof (int))
+ total++;
+
+ next_address += total;
+}
+
+/* Call here to output a chain of types.
+ After each function, this is done first for the chain of permanent types
+ made during the function, and then for the chain of temporary types.
+ This must be done before outputting the symbols and blocks of the function.
+
+ At the end of compilation, this is done for all the permanent types
+ made since the last function.
+
+ Each permanent type is done once, at the beginning of the next function,
+ or at the end of the compilation if no functions follow.
+ Once a type has been processed here, its TYPE_SYMTAB_ADDRESS remains
+ set up. */
+
+void
+symout_types (types)
+ tree types;
+{
+ struct typerec
+ {
+ int number;
+ int address;
+ int nfields;
+ int fields_address;
+ int name_address;
+ char *name;
+ char *name_prefix;
+ };
+
+ register int n_types, i;
+ register struct typerec *records;
+ register tree next;
+ struct type buffer;
+ int this_run_address = next_address;
+
+ /* Count the number of types to be handled here. */
+
+ for (next = types, n_types = 0;
+ next;
+ next = TREE_CHAIN (next), n_types++);
+
+ records = (struct typerec *) alloca (n_types * sizeof (struct typerec));
+
+ /* Compute the amount of space each type needs, updating next_address
+ and storing the address of the data for each type. */
+
+ for (next = types, i = 0;
+ next;
+ next = TREE_CHAIN (next), i++)
+ {
+ register struct typevec_elt *velt
+ = (struct typevec_elt *) xmalloc (sizeof (struct typevec_elt));
+ velt->next = typevec;
+ typevec = velt;
+
+ total_types++;
+
+ if (TYPE_NAME (next))
+ {
+ records[i].name_address = next_address;
+
+ if (TREE_CODE (TYPE_NAME (next)) == IDENTIFIER_NODE)
+ {
+ records[i].name = IDENTIFIER_POINTER (TYPE_NAME (next));
+ switch (TREE_CODE (next))
+ {
+ case RECORD_TYPE:
+ records[i].name_prefix = "struct ";
+ break;
+
+ case UNION_TYPE:
+ records[i].name_prefix = "union ";
+ break;
+
+ case ENUMERAL_TYPE:
+ records[i].name_prefix = "enum ";
+ break;
+ }
+ }
+ else
+ {
+ records[i].name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (next)));
+ records[i].name_prefix = 0;
+ }
+ symout_strings_skip (records[i].name_prefix, 0,
+ records[i].name, 0);
+
+ }
+ else
+ {
+ records[i].name = 0;
+ records[i].name_address = 0;
+ records[i].name_prefix = 0;
+ }
+
+ /* If this type was forward-referenced from a previous call
+ to symout_types, store this type's address into the reference. */
+ if (TYPE_POINTER_TO (next) != 0
+ && TYPE_SYMTAB_ADDRESS (TYPE_POINTER_TO (next)) != 0
+ && TYPE_SYMTAB_ADDRESS (TYPE_POINTER_TO (next)) < this_run_address)
+ {
+ int pos = ftell (symfile);
+ int myaddr = next_address;
+ fflush (symfile);
+ fseek (symfile,
+ (TYPE_SYMTAB_ADDRESS (TYPE_POINTER_TO (next))
+ + offsetof (struct type, target_type)),
+ 0);
+ fwrite (&myaddr, sizeof (int), 1, symfile);
+ fflush (symfile);
+ fseek (symfile, pos, 0);
+ }
+
+ records[i].address = next_address;
+ TYPE_SYMTAB_ADDRESS (next) = next_address;
+ velt->address = next_address;
+ next_address += sizeof (struct type);
+ records[i].nfields = 0;
+ records[i].fields_address = 0;
+ switch (TREE_CODE (next))
+ {
+ case ARRAY_TYPE:
+ records[i].nfields
+ = (TYPE_DOMAIN(next)
+ ? ! integer_zerop (TYPE_MIN_VALUE (TYPE_DOMAIN (next)))
+ : 0 );
+ break;
+
+ case INTEGER_TYPE:
+ if (subrange_p (next))
+ buffer.nfields = 2;
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case ENUMERAL_TYPE:
+ records[i].nfields = list_length (TYPE_FIELDS (next));
+ }
+ if (records[i].nfields)
+ records[i].fields_address = next_address;
+ next_address += records[i].nfields * sizeof (struct field);
+ }
+
+ /* Now write the data whose space we have assigned.
+ First fill the data into BUFFER, then write BUFFER. */
+
+ for (next = types, i = 0;
+ next;
+ next = TREE_CHAIN (next), i++)
+ {
+ if (records[i].name)
+ symout_strings_print (records[i].name_prefix, 0,
+ records[i].name, 0);
+
+ if (TREE_TYPE (next) != 0 && TYPE_OUTPUT_ADDRESS (TREE_TYPE (next)) == 0)
+ {
+ /* We are making a forward-reference to our target type.
+ Make a list of all of these. */
+ if (TREE_PERMANENT (next))
+ permanent_fwd_refs
+ = perm_tree_cons (TREE_TYPE (next), 0, permanent_fwd_refs);
+ else
+ temporary_fwd_refs
+ = tree_cons (TREE_TYPE (next), 0, temporary_fwd_refs);
+ }
+
+ if (TYPE_SIZE (next) == 0)
+ buffer.length = 0;
+ else
+ buffer.length
+ = (TREE_INT_CST_LOW (TYPE_SIZE (next))
+ * TYPE_SIZE_UNIT (next) / BITS_PER_UNIT);
+
+ buffer.name = (char *) records[i].name_address;
+ buffer.target_type = (struct type *) (TREE_TYPE (next) ? TYPE_OUTPUT_ADDRESS (TREE_TYPE (next)) : 0);
+
+ buffer.pointer_type = 0;
+ buffer.function_type = 0;
+ buffer.flags
+ = ((TREE_CODE (next) == INTEGER_TYPE || TREE_CODE (next) == ENUMERAL_TYPE)
+ && TREE_UNSIGNED (next))
+ ? TYPE_FLAG_UNSIGNED : 0;
+ buffer.nfields = records[i].nfields;
+ buffer.fields = (struct field *) records[i].fields_address;
+
+ switch (TREE_CODE (next))
+ {
+ case INTEGER_TYPE:
+ buffer.code = TYPE_CODE_INT;
+ if (buffer.nfields)
+ buffer.code = TYPE_CODE_RANGE;
+ break;
+
+ case REAL_TYPE:
+ buffer.code = TYPE_CODE_FLT;
+ break;
+
+ case VOID_TYPE:
+ buffer.code = TYPE_CODE_VOID;
+ break;
+
+ case POINTER_TYPE:
+ buffer.code = TYPE_CODE_PTR;
+ break;
+
+ case ARRAY_TYPE:
+ if (buffer.nfields == 0)
+ buffer.code = TYPE_CODE_ARRAY;
+ else
+ buffer.code = TYPE_CODE_PASCAL_ARRAY;
+ break;
+
+ case RECORD_TYPE:
+ buffer.code = TYPE_CODE_STRUCT;
+ break;
+
+ case UNION_TYPE:
+ buffer.code = TYPE_CODE_UNION;
+ break;
+
+ case FUNCTION_TYPE:
+ buffer.code = TYPE_CODE_FUNC;
+ break;
+
+ case ENUMERAL_TYPE:
+ buffer.code = TYPE_CODE_ENUM;
+ break;
+
+ default:
+ abort ();
+ }
+
+ fwrite (&buffer, sizeof buffer, 1, symfile);
+
+ /* Now write the `struct field's that certain kinds of type have.
+ This allocates space for the names of those fields,
+ incrementing next_address for the names. */
+
+ switch (TREE_CODE (next))
+ {
+ case ARRAY_TYPE:
+ if (buffer.nfields)
+ symout_array_domain (next);
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ symout_record_fields (next);
+ break;
+
+ case ENUMERAL_TYPE:
+ symout_enum_values (next);
+ break;
+
+ case INTEGER_TYPE:
+ if (buffer.nfields)
+ symout_range_bounds (next);
+ }
+ }
+
+ /* Now output the strings referred to by the fields of certain types.
+ (next_address was already updated for these strings.) */
+
+ for (next = types, i = 0;
+ next;
+ next = TREE_CHAIN (next), i++)
+ {
+ switch (TREE_CODE (next))
+ {
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ symout_record_field_names (next);
+ break;
+
+ case ENUMERAL_TYPE:
+ symout_enum_value_names (next);
+ break;
+ }
+ }
+}
+
+/* Given a list of types TYPES, return a chain of just those
+ that haven't been written in the symbol table. */
+
+static tree
+filter_undefined_types (types)
+ tree types;
+{
+ tree new = 0;
+ tree next;
+
+ for (next = types; next; next = TREE_CHAIN (next))
+ if (TYPE_SYMTAB_ADDRESS (TREE_PURPOSE (next)) == 0)
+ {
+ TREE_CHAIN (TREE_PURPOSE (next)) = new;
+ new = TREE_PURPOSE (next);
+ }
+
+ return new;
+}
+
+/* Return nonzero if TYPE's range of possible values
+ is not the full range allowed by the number of bits it has.
+ TYPE is assumed to be an INTEGER_TYPE or ENUMERAL_TYPE. */
+
+static int
+subrange_p (type)
+ tree type;
+{
+ int uns = TREE_UNSIGNED (type);
+
+ if (TYPE_PRECISION (type) >= HOST_BITS_PER_INT)
+ {
+ if (uns)
+ return integer_zerop (TYPE_MIN_VALUE (type))
+ && TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) == 0
+ && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (type))
+ == (1 << (TYPE_PRECISION (type) - HOST_BITS_PER_INT)) - 1);
+ return TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) == 0
+ && TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) == 0
+ && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (type))
+ == (-1) << (TYPE_PRECISION (type) - 1 - HOST_BITS_PER_INT))
+ && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (type))
+ == (1 << (TYPE_PRECISION (type) - 1 - HOST_BITS_PER_INT)) - 1);
+ }
+
+ if (uns)
+ {
+ int mask;
+
+ if (TYPE_PRECISION (type) == HOST_BITS_PER_INT)
+ /* Shifting by 32 loses on some machines. */
+ mask = -1;
+ else
+ mask = (1 << TYPE_PRECISION (type)) - 1;
+
+ return (integer_zerop (TYPE_MIN_VALUE (type))
+ && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)) == mask));
+ }
+ else
+ return ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (type))
+ == (-1) << (TYPE_PRECISION (type) - 1))
+ && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (type))
+ == (1 << (TYPE_PRECISION (type) - 1)) - 1));
+}
+
+/* Functions to output the "fields" of various kinds of types.
+ These assume that next_address has already been incremented to
+ cover these fields, and the fields of all the other types being
+ output in this batch; so next_address can be used to allocate
+ space to store field names, etc. */
+
+static void
+symout_array_domain (type)
+ tree type;
+{
+ struct field buffer;
+
+ buffer.bitpos = 0;
+ buffer.bitsize = 0;
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TYPE_DOMAIN (type));
+ buffer.name = 0;
+ fwrite (&buffer, sizeof (struct field), 1, symfile);
+}
+
+static void
+symout_range_bounds (type)
+ tree type;
+{
+ struct field buffer;
+
+ buffer.bitpos = TREE_INT_CST_LOW (TYPE_MIN_VALUE (type));
+ buffer.bitsize = 0;
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (type);
+ buffer.name = 0;
+ fwrite (&buffer, sizeof (struct field), 1, symfile);
+
+ buffer.bitpos = TREE_INT_CST_LOW (TYPE_MAX_VALUE (type));
+ buffer.bitsize = 0;
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (type);
+ buffer.name = 0;
+ fwrite (&buffer, sizeof (struct field), 1, symfile);
+}
+
+static void
+symout_record_fields (type)
+ tree type;
+{
+ struct field buffer;
+ register tree field;
+
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ {
+ buffer.bitpos = DECL_OFFSET (field);
+ buffer.bitsize
+ = (TREE_PACKED (field)
+ ? TREE_INT_CST_LOW (DECL_SIZE (field)) * DECL_SIZE_UNIT (field)
+ : 0);
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TREE_TYPE (field));
+ if (DECL_NAME (field))
+ {
+ buffer.name = (char *) next_address;
+ symout_strings_skip (0, IDENTIFIER_LENGTH (DECL_NAME (field)), 0, 0);
+ }
+ else
+ buffer.name = 0;
+ fwrite (&buffer, sizeof (struct field), 1, symfile);
+ }
+}
+
+static void
+symout_enum_values (type)
+ tree type;
+{
+ struct field buffer;
+ register tree link, value;
+
+ for (link = TYPE_VALUES (type); link; link = TREE_CHAIN (link))
+ {
+ value = TREE_VALUE (link);
+ buffer.bitpos = TREE_INT_CST_LOW (value);
+ buffer.bitsize = 0;
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (type);
+ buffer.name = (char *) next_address;
+ symout_strings_skip (0, IDENTIFIER_LENGTH (TREE_PURPOSE (link)), 0, 0);
+ fwrite (&buffer, sizeof buffer, 1, symfile);
+ }
+}
+
+/* Output field names or value names for the fields of a type.
+ This is called, for the types that need it, after the fields
+ have been output for all the types in the batch.
+ We do not update next_address here, because it has already been
+ updated for all the names in all the fields in all the types. */
+
+static void
+symout_record_field_names (type)
+ tree type;
+{
+ register tree field;
+
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (DECL_NAME (field))
+ symout_strings_print (IDENTIFIER_POINTER (DECL_NAME (field)),
+ IDENTIFIER_LENGTH (DECL_NAME (field)),
+ 0, 0);
+}
+
+static void
+symout_enum_value_names (type)
+ tree type;
+{
+ register tree value;
+
+ for (value = TYPE_VALUES (type); value; value = TREE_CHAIN (value))
+ symout_strings_print (IDENTIFIER_POINTER (TREE_PURPOSE (value)),
+ IDENTIFIER_LENGTH (TREE_PURPOSE (value)),
+ 0, 0);
+}
+
+/* Output the symbols of a block, given the list of decl nodes.
+ Store the file addresses at which the symbols are output
+ into ADDR_BUFFER, a vector which has just the right length.
+
+ If FILTER is 1, do only the private symbols in DECLS.
+ If FILTER is 2, do only the public ones (but no externals).
+ If FILTER is 0, do all (except external functions). */
+
+static void
+symout_block_symbols (decls, addr_buffer, filter)
+ tree decls;
+ int *addr_buffer;
+ int filter;
+{
+ register tree decl;
+ struct symbol buffer;
+ register int i;
+
+ for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl))
+ {
+ register int name_address = next_address;
+
+ if (filter == (TREE_PUBLIC (decl) ? 1 : 2))
+ continue;
+
+ /* Do not mention external functions.
+ Let their own files mention them.
+ In the top blocks, don't mention external anything. */
+
+ if (TREE_EXTERNAL (decl)
+ && (filter || TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE))
+ continue;
+
+ if (TREE_TYPE (decl) == error_mark_node)
+ continue;
+
+ symout_strings (IDENTIFIER_POINTER (DECL_NAME (decl)),
+ IDENTIFIER_LENGTH (DECL_NAME (decl)),
+ 0, 0);
+ addr_buffer[i] = next_address;
+ buffer.name = (char *) name_address;
+ buffer.namespace = VAR_NAMESPACE;
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TREE_TYPE (decl));
+ switch (TREE_CODE (decl))
+ {
+ case PARM_DECL:
+ buffer.class = LOC_ARG;
+ buffer.value.value = DECL_OFFSET (decl) / BITS_PER_UNIT;
+ break;
+
+ case VAR_DECL:
+ case RESULT_DECL:
+ if (TREE_STATIC (decl) || TREE_EXTERNAL (decl))
+ {
+ if (! TREE_PUBLIC (decl) || DECL_INITIAL (decl))
+ {
+ char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ fprintf (asmfile, "\t.gdbsym ");
+ ASM_OUTPUT_LABELREF (asmfile, str);
+ fprintf (asmfile, ",%d\n",
+ next_address + (char *)&buffer.value - (char *)&buffer);
+ buffer.class = LOC_STATIC;
+ }
+ else
+ /* Uninitialized public symbols are output as .comm;
+ Tell GDB to get address from loader global symbol.
+ Also come here for symbols declared extern. */
+ buffer.class = LOC_EXTERNAL;
+ }
+ else
+ {
+ if (GET_CODE (DECL_RTL (decl)) == REG)
+ {
+ buffer.class = LOC_REGISTER;
+ buffer.value.value = REGNO (DECL_RTL (decl));
+ /* Detect vars that were optimized entirely away. */
+ if (buffer.value.value == -1)
+ buffer.class = LOC_CONST;
+ }
+ else if (GET_CODE (DECL_RTL (decl)) == MEM
+ && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
+ || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG
+ && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM)))
+ /* If the value is indirect by memory or by a register
+ that isn't the frame pointer
+ then it means the object is variable-sized and address through
+ that register or stack slot.
+ If we have a pointer-type (which we should, for an array),
+ output the variable as a pointer.
+ Otherwise ignore it, since it is hard to create the ptr
+ type now and output it, and -gg is being retired. */
+ {
+ tree ptype = TYPE_POINTER_TO (TREE_TYPE (TREE_TYPE (decl)));
+ if (ptype == 0
+ || TYPE_OUTPUT_ADDRESS (ptype) == 0)
+ continue;
+
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (ptype);
+
+
+ if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG)
+ {
+ buffer.class = LOC_REGISTER;
+ buffer.value.value = REGNO (DECL_RTL (decl));
+ /* Detect vars that were optimized entirely away. */
+ if (buffer.value.value == -1)
+ buffer.class = LOC_CONST;
+ }
+ else
+ {
+ register rtx addr = XEXP (DECL_RTL (decl), 0);
+ if (GET_CODE (addr) != PLUS && GET_CODE (addr) != MINUS)
+ abort ();
+ if (GET_CODE (XEXP (addr, 1)) != CONST_INT)
+ abort ();
+ buffer.class = LOC_LOCAL;
+ buffer.value.value = INTVAL (XEXP (addr, 1));
+ if (GET_CODE (addr) == MINUS)
+ buffer.value.value = - buffer.value.value;
+ }
+ }
+ /* Locals in memory are expected to be addressed as
+ (PLUS (REG ...) (CONST_INT ...)).
+ Bomb out if that is not so. */
+ else if (GET_CODE (DECL_RTL (decl)) == MEM)
+ {
+ register rtx addr = XEXP (DECL_RTL (decl), 0);
+ if (GET_CODE (addr) != PLUS && GET_CODE (addr) != MINUS)
+ abort ();
+ if (GET_CODE (XEXP (addr, 1)) != CONST_INT)
+ abort ();
+ buffer.class = LOC_LOCAL;
+ buffer.value.value = INTVAL (XEXP (addr, 1));
+ if (GET_CODE (addr) == MINUS)
+ buffer.value.value = - buffer.value.value;
+ }
+ else
+ abort ();
+ }
+ break;
+
+ case TYPE_DECL:
+ buffer.class = LOC_TYPEDEF;
+ buffer.value.value = 0;
+ break;
+
+ case CONST_DECL:
+ buffer.class = LOC_CONST;
+ buffer.value.value = TREE_INT_CST_LOW (DECL_INITIAL (decl));
+ break;
+
+ case FUNCTION_DECL:
+ if (DECL_INITIAL (decl))
+ {
+ buffer.class = LOC_BLOCK;
+ buffer.value.value = DECL_BLOCK_SYMTAB_ADDRESS (decl);
+ }
+ else
+ buffer.class = LOC_EXTERNAL;
+ }
+
+ fwrite (&buffer, sizeof buffer, 1, symfile);
+ next_address += sizeof buffer;
+ i++;
+ }
+}
+
+/* Output the tags (struct, union and enum definitions) for a block,
+ given a list of them (a chain of TREE_LIST nodes) in TAGS.
+ Store their addresses in the file into ADDR_BUFFER. */
+
+static void
+symout_block_tags (tags, addr_buffer)
+ tree tags;
+ int *addr_buffer;
+{
+ register tree tag;
+ struct symbol buffer;
+ register int i;
+
+ for (tag = tags, i = 0; tag; tag = TREE_CHAIN (tag), i++)
+ {
+ buffer.name = (char *) next_address;
+
+ symout_strings (IDENTIFIER_POINTER (TREE_PURPOSE (tag)),
+ IDENTIFIER_LENGTH (TREE_PURPOSE (tag)),
+ 0, 0);
+ addr_buffer[i] = next_address;
+ buffer.namespace = STRUCT_NAMESPACE;
+ buffer.type = (struct type *) TYPE_OUTPUT_ADDRESS (TREE_VALUE (tag));
+ buffer.class = LOC_TYPEDEF;
+ buffer.value.value = 0;
+
+ fwrite (&buffer, sizeof buffer, 1, symfile);
+ next_address += sizeof buffer;
+ }
+}
+
+/* Output all the data structure for a "block"
+ (any binding contour).
+ DECLS is the chain of declarations of variables in this block.
+ TAGS is the list of struct, union and enum tag definitions of this block.
+ SUPERBLOCK_ADDRESS is the symtab file address of the containing block's
+ data structure. */
+
+int
+symout_block (decls, tags, args, superblock_address)
+ tree decls;
+ tree tags;
+ tree args;
+ int superblock_address;
+{
+ register tree decl;
+ register int i;
+ register int *addr_buffer;
+ struct block buffer;
+ int n_decls, n_tags, n_args, total;
+ register struct blockvec_elt *velt;
+ int block_address;
+
+ for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl))
+ if (! TREE_EXTERNAL (decl)
+ || TREE_CODE (TREE_TYPE (decl)) != FUNCTION_TYPE)
+ i++;
+
+ n_decls = i;
+
+ for (decl = args, i = 0; decl; decl = TREE_CHAIN (decl), i++);
+ n_args = i;
+
+ for (decl = tags, i = 0; decl; decl = TREE_CHAIN (decl), i++);
+ n_tags = i;
+
+ total = n_decls + n_args + n_tags;
+
+ addr_buffer = (int *) alloca (total * sizeof (int));
+
+ symout_block_symbols (args, addr_buffer, 0);
+ symout_block_symbols (decls, addr_buffer + n_args, 0);
+ symout_block_tags (tags, addr_buffer + n_decls + n_args);
+
+ velt = (struct blockvec_elt *) xmalloc (sizeof (struct blockvec_elt));
+ velt->next = blockvec;
+ velt->address = next_address;
+ blockvec = velt;
+
+ buffer.startaddr = 0;
+ buffer.endaddr = 0;
+ buffer.superblock = (struct block *) superblock_address;
+ buffer.function = 0;
+ buffer.nsyms = total;
+
+ block_address = next_address;
+ fwrite (&buffer, sizeof buffer - sizeof buffer.sym, 1, symfile);
+ next_address += sizeof buffer - sizeof buffer.sym;
+
+ fwrite (addr_buffer, sizeof (int), total, symfile);
+ next_address += total * sizeof (int);
+
+ fprintf (asmfile, "\t.gdbblock %d,%d\n", total_blocks + 2, block_address);
+ total_blocks++;
+
+ return block_address;
+}
+
+/* Walk STMT, the body of a function, and output symtab data on
+ all the blocks that compose it and all symbols inside them.
+ ARGS is a chain of decls for argument variables of the function.
+ SUPERBLOCK_ADDRESS is the address of symbol data for the
+ innermost block containing STMT; it is used for recursive calls,
+ and is always 0 for the outermost call (since the containing
+ block for a function is output later than the function). */
+
+int
+symout_function (stmt, args, superblock_address)
+ register tree stmt;
+ tree args;
+ int superblock_address;
+{
+ int address = superblock_address;
+
+ while (stmt)
+ {
+ switch (TREE_CODE (stmt))
+ {
+ case COMPOUND_STMT:
+ case LOOP_STMT:
+ symout_function (STMT_BODY (stmt), 0, address);
+ break;
+
+ case IF_STMT:
+ symout_function (STMT_THEN (stmt), 0, address);
+ symout_function (STMT_ELSE (stmt), 0, address);
+ break;
+
+ case LET_STMT:
+ /* Ignore LET_STMTs for blocks never really used to make RTL. */
+ if (! TREE_USED (stmt))
+ break;
+ address =
+ symout_block (STMT_VARS (stmt), STMT_TYPE_TAGS (stmt), args,
+ superblock_address);
+
+ symout_function (STMT_SUBBLOCKS (stmt), 0, address);
+ }
+ stmt = TREE_CHAIN (stmt);
+ }
+ return address;
+}
+
+symout_function_end ()
+{
+ /* Output dummy entries for any undefined structure references. */
+ symout_types (filter_undefined_types (temporary_fwd_refs));
+ temporary_fwd_refs = 0;
+}
+
+/* Output all the data structure for a top two blocks in a compilation.
+ The top block is for public (global) symbols;
+ the next one is for private (this file only) symbols.
+
+ DECLS is the chain of declarations of variables in this block.
+ TAGS is the list of struct, union and enum tag definitions. */
+
+void
+symout_top_blocks (decls, tags)
+ tree decls;
+ tree tags;
+{
+ register tree decl;
+ register int i;
+ register int *addr_buffer;
+ struct block buffer;
+ int n_decls, n_tags;
+ register struct blockvec_elt *velt;
+ int top_block_addr;
+
+ /* First do the public-symbols block. */
+
+ for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl))
+ if (TREE_PUBLIC (decl) && ! TREE_EXTERNAL (decl))
+ i++;
+ n_decls = i;
+
+ addr_buffer = (int *) alloca (n_decls * sizeof (int));
+
+ symout_block_symbols (decls, addr_buffer, 2);
+
+ fprintf (asmfile, ".text 0\n\t.gdbend 0\n");
+ fprintf (asmfile, "\t.gdbblock 0,%d\n", next_address);
+
+ total_blocks++;
+ velt = (struct blockvec_elt *) xmalloc (sizeof (struct blockvec_elt));
+ velt->next = blockvec;
+ velt->address = next_address;
+ blockvec = velt;
+
+ top_block_addr = next_address;
+
+ buffer.startaddr = 0;
+ buffer.endaddr = 0;
+ buffer.superblock = 0;
+ buffer.function = 0;
+ buffer.nsyms = n_decls;;
+
+ fwrite (&buffer, sizeof buffer - sizeof buffer.sym, 1, symfile);
+ next_address += sizeof buffer - sizeof buffer.sym;
+
+ fwrite (addr_buffer, sizeof (int), n_decls, symfile);
+ next_address += n_decls * sizeof (int);
+
+ /* Next do the private-symbols block. */
+
+ for (decl = decls, i = 0; decl; decl = TREE_CHAIN (decl))
+ if (! TREE_PUBLIC (decl) && ! TREE_EXTERNAL (decl))
+ i++;
+ n_decls = i;
+
+ for (decl = tags, i = 0; decl; decl = TREE_CHAIN (decl), i++);
+ n_tags = i;
+
+ addr_buffer = (int *) alloca ((n_decls + n_tags) * sizeof (int));
+
+ symout_block_symbols (decls, addr_buffer, 1);
+ symout_block_tags (tags, addr_buffer + n_decls);
+
+ fprintf (asmfile, "\t.gdbend 1\n");
+ fprintf (asmfile, "\t.gdbblock 1,%d\n", next_address);
+
+ total_blocks++;
+ velt = (struct blockvec_elt *) xmalloc (sizeof (struct blockvec_elt));
+ velt->next = blockvec;
+ velt->address = next_address;
+ blockvec = velt;
+
+ buffer.startaddr = 0;
+ buffer.endaddr = 0;
+ buffer.superblock = (struct block *) top_block_addr;
+ buffer.function = 0;
+ buffer.nsyms = n_decls + n_tags;;
+
+ fwrite (&buffer, sizeof buffer - sizeof buffer.sym, 1, symfile);
+ next_address += sizeof buffer - sizeof buffer.sym;
+
+ fwrite (addr_buffer, sizeof (int), n_decls + n_tags, symfile);
+ next_address += (n_decls + n_tags) * sizeof (int);
+}
+
+/* Output the source-line-number information. */
+
+/* Output a `struct source' for the source file described by F.
+ Return the address-in-the-symseg of the `struct source'. */
+
+static int
+symout_source_file (f)
+ struct gdbfile *f;
+{
+ /* Make the `struct source' big enough for as many lines as
+ this file has. */
+ int size = sizeof (struct source) + (f->nlines - 1) * sizeof (struct line);
+ struct source *buffer
+ = (struct source *) alloca (size);
+ int addr;
+
+ /* Use zero for the line data, since assembler will store the real data. */
+ bzero (buffer, size);
+
+ /* Output the file's name as a string. The assembler doesn't know this. */
+ buffer->name = (char *) next_address;
+ symout_strings (f->name, 0, 0, 0);
+ buffer->nlines = f->nlines;
+
+ /* Write the structure. */
+ addr = next_address;
+ fwrite (buffer, 1, size, symfile);
+ next_address += size;
+
+ /* Tell assembler where to write the real line-number data. */
+ fprintf (asmfile, "\t.gdblinetab %d,%d\n",
+ f->filenum, addr + sizeof (int));
+
+ return addr;
+}
+
+/* Output the `struct sourcevector' which describes all the
+ source files and points a `struct source' for each one. */
+
+static int
+symout_sources ()
+{
+ register struct gdbfile *f;
+ int nfiles = 0;
+ struct sourcevector *s;
+ int i;
+ int size;
+ int addr;
+
+ /* Count number of files to determine size of the sourcevector. */
+ for (f = gdbfiles; f; f = f->next)
+ ++nfiles;
+
+ /* Allocate buffer for the sourcevector and record its length. */
+ size = sizeof (int) + nfiles * sizeof (struct source *);
+ s = (struct sourcevector *) alloca (size);
+ s->length = nfiles;
+
+ /* Output a `struct source' for each file; put address into sourcevector. */
+ for (f = gdbfiles, i = 0; f; f = f->next, i++)
+ s->source[i] = (struct source *) symout_source_file (f);
+
+ /* Output the sourcevector. */
+ addr = next_address;
+ fwrite (s, 1, size, symfile);
+ next_address += size;
+ return addr;
+}
+
+/* Call here at the end of compilation, after outputting all the
+ blocks and symbols, to output the blockvector and typevector
+ and close the symbol table file. FILETIME is source file's
+ creation time. */
+
+void
+symout_finish (filename, filetime)
+ char *filename;
+ int filetime;
+{
+ int *blockvector = (int *) alloca ((total_blocks + 1) * sizeof (int));
+ int *typevector;
+ int now = time (0);
+ register int i;
+ struct symbol_root buffer;
+ char dir[MAXNAMLEN];
+
+ /* Output dummy entries for any undefined structure references. */
+ symout_types (filter_undefined_types (permanent_fwd_refs));
+
+ typevector = (int *) alloca ((total_types + 1) * sizeof (int));
+
+ buffer.language = language_c;
+ buffer.blockvector = (struct blockvector *) next_address;
+
+ /* The two blocks at the beginning of the chain
+ are the file's private symbols block and public symbols block.
+ They belong at the front of the blockvector, in that order. */
+ blockvector[2] = blockvec->address;
+ blockvec = blockvec->next;
+ blockvector[1] = blockvec->address;
+ blockvec = blockvec->next;
+
+ /* The rest of the blocks are in the chain in reverse order. */
+ for (i = total_blocks; i > 2; i--)
+ {
+ blockvector[i] = blockvec->address;
+ blockvec = blockvec->next;
+ }
+ blockvector[0] = total_blocks;
+
+ fwrite (blockvector, sizeof (int), total_blocks + 1, symfile);
+ next_address += sizeof (int) * (total_blocks + 1);
+
+ buffer.typevector = (struct typevector *) next_address;
+
+ for (i = total_types; i > 0; i--)
+ {
+ typevector[i] = typevec->address;
+ typevec = typevec->next;
+ }
+ typevector[0] = total_types;
+
+ fwrite (typevector, sizeof (int), total_types + 1, symfile);
+ next_address += sizeof (int) * (total_types + 1);
+
+ buffer.sourcevector = (struct sourcevector *) symout_sources ();
+
+ buffer.format = 1;
+ buffer.textrel = 0; /* These four will be set up by linker. */
+ buffer.datarel = 0; /* Make them 0 now, which is right for */
+ buffer.bssrel = 0; /* looking at the .o file in gdb. */
+ buffer.ldsymoff = 0;
+
+ buffer.version = (char *) next_address;
+ symout_strings (ctime (&filetime), 0, 0, 0);
+
+ buffer.compilation = (char *) next_address;
+ symout_strings (ctime (&now), 0, 0, 0);
+
+ buffer.filename = (char *) next_address;
+ symout_strings (filename, 0, 0, 0);
+
+ buffer.filedir = (char *) next_address;
+#ifdef USG
+ strcpy (dir, getcwd (dir, MAXNAMLEN));
+#else
+#ifndef VMS
+ getwd (dir);
+#else
+ abort ();
+#endif
+#endif
+ symout_strings (dir, 0, 0, 0);
+
+ fflush (symfile);
+
+ if (ferror (symfile) != 0)
+ fatal_io_error (symfile_name);
+
+ buffer.length = next_address;
+
+ if (lseek (fileno (symfile), 0, 0) < 0)
+ pfatal_with_name (symfile_name);
+ if (write (fileno (symfile), &buffer, sizeof buffer) < 0)
+ pfatal_with_name (symfile_name);
+ close (fileno (symfile));
+}