diff --git a/binutils/.Sanitize b/binutils/.Sanitize index aca0ca803b..aaf5e0118a 100644 --- a/binutils/.Sanitize +++ b/binutils/.Sanitize @@ -93,6 +93,7 @@ syslex.l sysroff.info testsuite version.c +wrstabs.c Things-to-lose: diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 200a4c8092..38a9db1a01 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,51 @@ +Wed Mar 20 16:35:20 1996 Ian Lance Taylor + + * wrstabs.c: New file. + * budbg.h (write_stabs_in_sections_debugging_info): Declare. + * objcopy.c (write_debugging_info): For COFF or ELF, output stabs + in sections. + * Makefile.in: Rebuild dependencies. + (CFILES): Add wrstabs.c. + (WRITE_DEBUG_OBJS): New variable. + ($(OBJCOPY_PROG)): Use $(WRITE_DEBUG_OBJS), not $(DEBUG_OBJS). + ($(STRIP_PROG)): Likewise. + + * stabs.c (parse_stab_members): Make type stub detection more like + gdb. + + * ieee.c (struct ieee_handle): Add fields complex_float_index and + complex_double_index. + (ieee_complex_type): Cache type index in complex_float_index and + complex_double_index, depending upon size. Set size on type stack + to size * 2. + + * ieee.c (ieee_empty_type): Use builtin_unknown, not 0. + (ieee_void_type): Use builtin_void, not 1. + + * ieee.c (parse_ieee_ty): Handle 'V' type code. + (parse_ieee_atn): Don't require two numbers for type 10. + + * ieee.c (parse_ieee_be): Add one to offset at end of function or + block. + + * ieee.c (struct ieee_block): Add field skip. + (parse_ieee_bb): Don't call debug_record_function for __XRYCPP + function, and set skip field. + (parse_ieee_be): Don't call debug_end_function if skip is set. + + * debug.c (struct debug_handle): Add fields current_write_lineno + and current_write_lineno_index. + (debug_write): Initialize current_write_lineno and + current_write_lineno_index for each unit. Call + debug_write_linenos rather than writing out the line numbers + directly. + (debug_write_function): Call debug_write_linenos. + (debug_write_block): Likewise. + (debug_write_linenos): New static function. + + * debug.c (debug_write_type): For DEBUG_KIND_FUNCTION, push return + type before arguments. + Mon Mar 18 18:05:33 1996 Ian Lance Taylor * configure.in: Add AC_FUNC_VFORK. diff --git a/binutils/wrstabs.c b/binutils/wrstabs.c new file mode 100644 index 0000000000..985b700963 --- /dev/null +++ b/binutils/wrstabs.c @@ -0,0 +1,2407 @@ +/* wrstabs.c -- Output stabs debugging information + Copyright (C) 1996 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GNU Binutils. + + 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. */ + +/* This file contains code which writes out stabs debugging + information. */ + +#include +#include +#include + +#include "bfd.h" +#include "bucomm.h" +#include "libiberty.h" +#include "debug.h" +#include "budbg.h" + +/* Meaningless definition needs by aout64.h. FIXME. */ +#define BYTES_IN_WORD 4 + +#include "aout/aout64.h" +#include "aout/stab_gnu.h" + +/* The size of a stabs symbol. This presumes 32 bit values. */ + +#define STAB_SYMBOL_SIZE (12) + +/* An entry in a string hash table. */ + +struct string_hash_entry +{ + struct bfd_hash_entry root; + /* Index in string table. */ + long index; + /* Size of type if this is a typedef. */ + unsigned int size; +}; + +/* A string hash table. */ + +struct string_hash_table +{ + struct bfd_hash_table table; +}; + +/* The type stack. Each element on the stack is a string. */ + +struct stab_type_stack +{ + /* The next element on the stack. */ + struct stab_type_stack *next; + /* This element as a string. */ + char *string; + /* The type index of this element. */ + long index; + /* The size of the type. */ + unsigned int size; + /* Whether type string defines a new type. */ + boolean definition; + /* String defining struct fields. */ + char *fields; + /* NULL terminated array of strings defining base classes for a + class. */ + char **baseclasses; + /* String defining class methods. */ + char *methods; + /* String defining vtable pointer for a class. */ + char *vtable; +}; + +/* This structure is used to keep track of type indices for tagged + types. */ + +struct stab_tag +{ + /* The type index. */ + long index; + /* The tag name. */ + const char *tag; + /* The kind of type. This is set to DEBUG_KIND_ILLEGAL when the + type is defined. */ + enum debug_type_kind kind; + /* The size of the struct. */ + unsigned int size; +}; + +/* We remember various sorts of type indices. They are not related, + but, for convenience, we keep all the information in this + structure. */ + +struct stab_type_cache +{ + /* The void type index. */ + long void_type; + /* Signed integer type indices, indexed by size - 1. */ + long signed_integer_types[8]; + /* Unsigned integer type indices, indexed by size - 1. */ + long unsigned_integer_types[8]; + /* Floating point types, indexed by size - 1. */ + long float_types[16]; + /* Pointers to types, indexed by the type index. */ + long *pointer_types; + size_t pointer_types_alloc; + /* Functions returning types, indexed by the type index. */ + long *function_types; + size_t function_types_alloc; + /* References to types, indexed by the type index. */ + long *reference_types; + size_t reference_types_alloc; + /* Struct/union/class type indices, indexed by the struct id. */ + struct stab_tag *struct_types; + size_t struct_types_alloc; +}; + +/* This is the handle passed through debug_write. */ + +struct stab_write_handle +{ + /* The BFD. */ + bfd *abfd; + /* This buffer holds the symbols. */ + bfd_byte *symbols; + size_t symbols_size; + size_t symbols_alloc; + /* This buffer holds the strings. */ + bfd_byte *strings; + size_t strings_size; + size_t strings_alloc; + /* This hash table eliminates duplicate strings. */ + struct string_hash_table strhash; + /* The type stack. */ + struct stab_type_stack *type_stack; + /* The next type index. */ + long type_index; + /* The type cache. */ + struct stab_type_cache type_cache; + /* A mapping from typedef names to type indices. */ + struct string_hash_table typedef_hash; + /* If this is not -1, it is the offset to the most recent N_SO + symbol, and the value of that symbol needs to be set. */ + long so_offset; + /* If this is not -1, it is the offset to the most recent N_FUN + symbol, and the value of that symbol needs to be set. */ + long fun_offset; + /* The last text section address seen. */ + bfd_vma last_text_address; + /* The block nesting depth. */ + unsigned int nesting; + /* The function address. */ + bfd_vma fnaddr; + /* A pending LBRAC symbol. */ + bfd_vma pending_lbrac; + /* The current line number file name. */ + const char *lineno_filename; +}; + +static struct bfd_hash_entry *string_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static boolean stab_write_symbol + PARAMS ((struct stab_write_handle *, int, int, bfd_vma, const char *)); +static boolean stab_push_string + PARAMS ((struct stab_write_handle *, const char *, long, boolean, + unsigned int)); +static boolean stab_push_defined_type + PARAMS ((struct stab_write_handle *, long, unsigned int)); +static char *stab_pop_type PARAMS ((struct stab_write_handle *)); +static boolean stab_modify_type + PARAMS ((struct stab_write_handle *, int, unsigned int, long **, size_t *)); +static long stab_get_struct_index + PARAMS ((struct stab_write_handle *, const char *, unsigned int, + enum debug_type_kind, unsigned int *)); +static boolean stab_class_method_var + PARAMS ((struct stab_write_handle *, const char *, enum debug_visibility, + boolean, boolean, boolean, bfd_vma, boolean)); + +static boolean stab_start_compilation_unit PARAMS ((PTR, const char *)); +static boolean stab_start_source PARAMS ((PTR, const char *)); +static boolean stab_empty_type PARAMS ((PTR)); +static boolean stab_void_type PARAMS ((PTR)); +static boolean stab_int_type PARAMS ((PTR, unsigned int, boolean)); +static boolean stab_float_type PARAMS ((PTR, unsigned int)); +static boolean stab_complex_type PARAMS ((PTR, unsigned int)); +static boolean stab_bool_type PARAMS ((PTR, unsigned int)); +static boolean stab_enum_type + PARAMS ((PTR, const char *, const char **, bfd_signed_vma *)); +static boolean stab_pointer_type PARAMS ((PTR)); +static boolean stab_function_type PARAMS ((PTR, int, boolean)); +static boolean stab_reference_type PARAMS ((PTR)); +static boolean stab_range_type PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma)); +static boolean stab_array_type + PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma, boolean)); +static boolean stab_set_type PARAMS ((PTR, boolean)); +static boolean stab_offset_type PARAMS ((PTR)); +static boolean stab_method_type PARAMS ((PTR, boolean, int, boolean)); +static boolean stab_const_type PARAMS ((PTR)); +static boolean stab_volatile_type PARAMS ((PTR)); +static boolean stab_start_struct_type + PARAMS ((PTR, const char *, unsigned int, boolean, unsigned int)); +static boolean stab_struct_field + PARAMS ((PTR, const char *, bfd_vma, bfd_vma, enum debug_visibility)); +static boolean stab_end_struct_type PARAMS ((PTR)); +static boolean stab_start_class_type + PARAMS ((PTR, const char *, unsigned int, boolean, unsigned int, boolean, + boolean)); +static boolean stab_class_static_member + PARAMS ((PTR, const char *, const char *, enum debug_visibility)); +static boolean stab_class_baseclass + PARAMS ((PTR, bfd_vma, boolean, enum debug_visibility)); +static boolean stab_class_start_method PARAMS ((PTR, const char *)); +static boolean stab_class_method_variant + PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean, + bfd_vma, boolean)); +static boolean stab_class_static_method_variant + PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean)); +static boolean stab_class_end_method PARAMS ((PTR)); +static boolean stab_end_class_type PARAMS ((PTR)); +static boolean stab_typedef_type PARAMS ((PTR, const char *)); +static boolean stab_tag_type + PARAMS ((PTR, const char *, unsigned int, enum debug_type_kind)); +static boolean stab_typdef PARAMS ((PTR, const char *)); +static boolean stab_tag PARAMS ((PTR, const char *)); +static boolean stab_int_constant PARAMS ((PTR, const char *, bfd_vma)); +static boolean stab_float_constant PARAMS ((PTR, const char *, double)); +static boolean stab_typed_constant PARAMS ((PTR, const char *, bfd_vma)); +static boolean stab_variable + PARAMS ((PTR, const char *, enum debug_var_kind, bfd_vma)); +static boolean stab_start_function PARAMS ((PTR, const char *, boolean)); +static boolean stab_function_parameter + PARAMS ((PTR, const char *, enum debug_parm_kind, bfd_vma)); +static boolean stab_start_block PARAMS ((PTR, bfd_vma)); +static boolean stab_end_block PARAMS ((PTR, bfd_vma)); +static boolean stab_end_function PARAMS ((PTR)); +static boolean stab_lineno + PARAMS ((PTR, const char *, unsigned long, bfd_vma)); + +static const struct debug_write_fns stab_fns = +{ + stab_start_compilation_unit, + stab_start_source, + stab_empty_type, + stab_void_type, + stab_int_type, + stab_float_type, + stab_complex_type, + stab_bool_type, + stab_enum_type, + stab_pointer_type, + stab_function_type, + stab_reference_type, + stab_range_type, + stab_array_type, + stab_set_type, + stab_offset_type, + stab_method_type, + stab_const_type, + stab_volatile_type, + stab_start_struct_type, + stab_struct_field, + stab_end_struct_type, + stab_start_class_type, + stab_class_static_member, + stab_class_baseclass, + stab_class_start_method, + stab_class_method_variant, + stab_class_static_method_variant, + stab_class_end_method, + stab_end_class_type, + stab_typedef_type, + stab_tag_type, + stab_typdef, + stab_tag, + stab_int_constant, + stab_float_constant, + stab_typed_constant, + stab_variable, + stab_start_function, + stab_function_parameter, + stab_start_block, + stab_end_block, + stab_end_function, + stab_lineno +}; + +/* Routine to create an entry in a string hash table. */ + +static struct bfd_hash_entry * +string_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct string_hash_entry *ret = (struct string_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct string_hash_entry *) NULL) + ret = ((struct string_hash_entry *) + bfd_hash_allocate (table, sizeof (struct string_hash_entry))); + if (ret == (struct string_hash_entry *) NULL) + return NULL; + + /* Call the allocation method of the superclass. */ + ret = ((struct string_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + + if (ret) + { + /* Initialize the local fields. */ + ret->index = -1; + ret->size = 0; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Look up an entry in a string hash table. */ + +#define string_hash_lookup(t, string, create, copy) \ + ((struct string_hash_entry *) \ + bfd_hash_lookup (&(t)->table, (string), (create), (copy))) + +/* Add a symbol to the stabs debugging information we are building. */ + +static boolean +stab_write_symbol (info, type, desc, value, string) + struct stab_write_handle *info; + int type; + int desc; + bfd_vma value; + const char *string; +{ + bfd_size_type strx; + bfd_byte sym[STAB_SYMBOL_SIZE]; + + if (string == NULL) + strx = 0; + else + { + struct string_hash_entry *h; + + h = string_hash_lookup (&info->strhash, string, true, false); + if (h == NULL) + { + fprintf (stderr, "string_hash_lookup failed: %s\n", + bfd_errmsg (bfd_get_error ())); + return false; + } + if (h->index != -1) + strx = h->index; + else + { + size_t len; + + strx = info->strings_size; + h->index = strx; + + len = strlen (string); + while (info->strings_size + len + 1 > info->strings_alloc) + { + info->strings_alloc *= 2; + info->strings = (bfd_byte *) xrealloc (info->strings, + info->strings_alloc); + } + strcpy (info->strings + info->strings_size, string); + info->strings_size += len + 1; + } + } + + /* This presumes 32 bit values. */ + bfd_put_32 (info->abfd, strx, sym); + bfd_put_8 (info->abfd, type, sym + 4); + bfd_put_8 (info->abfd, 0, sym + 5); + bfd_put_16 (info->abfd, desc, sym + 6); + bfd_put_32 (info->abfd, value, sym + 8); + + if (info->symbols_size + STAB_SYMBOL_SIZE > info->symbols_alloc) + { + info->symbols_alloc *= 2; + info->symbols = (bfd_byte *) xrealloc (info->symbols, + info->symbols_alloc); + } + + memcpy (info->symbols + info->symbols_size, sym, STAB_SYMBOL_SIZE); + + info->symbols_size += STAB_SYMBOL_SIZE; + + return true; +} + +/* Push a string on to the type stack. */ + +static boolean +stab_push_string (info, string, index, definition, size) + struct stab_write_handle *info; + const char *string; + long index; + boolean definition; + unsigned int size; +{ + struct stab_type_stack *s; + + s = (struct stab_type_stack *) xmalloc (sizeof *s); + s->string = xstrdup (string); + s->index = index; + s->definition = definition; + s->size = size; + + s->fields = NULL; + s->baseclasses = NULL; + s->methods = NULL; + s->vtable = NULL; + + s->next = info->type_stack; + info->type_stack = s; + + return true; +} + +/* Push a type index which has already been defined. */ + +static boolean +stab_push_defined_type (info, index, size) + struct stab_write_handle *info; + long index; + unsigned int size; +{ + char buf[20]; + + sprintf (buf, "%ld", index); + return stab_push_string (info, buf, index, false, size); +} + +/* Pop a type off the type stack. The caller is responsible for + freeing the string. */ + +static char * +stab_pop_type (info) + struct stab_write_handle *info; +{ + struct stab_type_stack *s; + char *ret; + + s = info->type_stack; + assert (s != NULL); + + info->type_stack = s->next; + + ret = s->string; + + free (s); + + return ret; +} + +/* The general routine to write out stabs in sections debugging + information. This accumulates the stabs symbols and the strings in + two obstacks. We can't easily write out the information as we go + along, because we need to know the section sizes before we can + write out the section contents. ABFD is the BFD and DHANDLE is the + handle for the debugging information. This sets *PSYMS to point to + the symbols, *PSYMSIZE the size of the symbols, *PSTRINGS to the + strings, and *PSTRINGSIZE to the size of the strings. */ + +boolean +write_stabs_in_sections_debugging_info (abfd, dhandle, psyms, psymsize, + pstrings, pstringsize) + bfd *abfd; + PTR dhandle; + bfd_byte **psyms; + bfd_size_type *psymsize; + bfd_byte **pstrings; + bfd_size_type *pstringsize; +{ + struct stab_write_handle info; + + info.abfd = abfd; + + info.symbols_size = 0; + info.symbols_alloc = 500; + info.symbols = (bfd_byte *) xmalloc (info.symbols_alloc); + + info.strings_size = 1; + info.strings_alloc = 500; + info.strings = (bfd_byte *) xmalloc (info.strings_alloc); + info.strings[0] = '\0'; + + if (! bfd_hash_table_init (&info.strhash.table, string_hash_newfunc) + || ! bfd_hash_table_init (&info.typedef_hash.table, string_hash_newfunc)) + { + fprintf (stderr, "bfd_hash_table_init_failed: %s\n", + bfd_errmsg (bfd_get_error ())); + return false; + } + + info.type_stack = NULL; + info.type_index = 1; + memset (&info.type_cache, 0, sizeof info.type_cache); + info.so_offset = -1; + info.fun_offset = -1; + info.last_text_address = 0; + info.nesting = 0; + info.fnaddr = 0; + info.pending_lbrac = (bfd_vma) -1; + + /* The initial symbol holds the string size. */ + if (! stab_write_symbol (&info, 0, 0, 0, (const char *) NULL)) + return false; + + /* Output an initial N_SO symbol. */ + info.so_offset = info.symbols_size; + if (! stab_write_symbol (&info, N_SO, 0, 0, bfd_get_filename (abfd))) + return false; + + if (! debug_write (dhandle, &stab_fns, (PTR) &info)) + return false; + + assert (info.pending_lbrac == (bfd_vma) -1); + + /* Output a trailing N_SO. */ + if (! stab_write_symbol (&info, N_SO, 0, info.last_text_address, + (const char *) NULL)) + return false; + + /* Put the string size in the initial symbol. */ + bfd_put_32 (abfd, info.strings_size, info.symbols + 8); + + *psyms = info.symbols; + *psymsize = info.symbols_size; + + *pstrings = info.strings; + *pstringsize = info.strings_size; + + return true; +} + +/* Start writing out information for a compilation unit. */ + +static boolean +stab_start_compilation_unit (p, filename) + PTR p; + const char *filename; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* We would normally output an N_SO symbol here. However, that + would force us to reset all of our type information. I think we + will be better off just outputting an N_SOL symbol, and not + worrying about splitting information between files. */ + + info->lineno_filename = filename; + + return stab_write_symbol (info, N_SOL, 0, 0, filename); +} + +/* Start writing out information for a particular source file. */ + +static boolean +stab_start_source (p, filename) + PTR p; + const char *filename; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* FIXME: The symbol's value is supposed to be the text section + address. However, we would have to fill it in later, and gdb + doesn't care, so we don't bother with it. */ + + info->lineno_filename = filename; + + return stab_write_symbol (info, N_SOL, 0, 0, filename); +} + +/* Push an empty type. This shouldn't normally happen. We just use a + void type. */ + +static boolean +stab_empty_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* We don't call stab_void_type if the type is not yet defined, + because that might screw up the typedef. */ + + if (info->type_cache.void_type != 0) + return stab_push_defined_type (info, info->type_cache.void_type, 0); + else + { + long index; + char buf[40]; + + index = info->type_index; + ++info->type_index; + + sprintf (buf, "%ld=%ld", index, index); + + return stab_push_string (info, buf, index, false, 0); + } +} + +/* Push a void type. */ + +static boolean +stab_void_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + if (info->type_cache.void_type != 0) + return stab_push_defined_type (info, info->type_cache.void_type, 0); + else + { + long index; + char buf[40]; + + index = info->type_index; + ++info->type_index; + + info->type_cache.void_type = index; + + sprintf (buf, "%ld=%ld", index, index); + + return stab_push_string (info, buf, index, true, 0); + } +} + +/* Push an integer type. */ + +static boolean +stab_int_type (p, size, unsignedp) + PTR p; + unsigned int size; + boolean unsignedp; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long *cache; + + if (size <= 0 || (size > sizeof (long) && size != 8)) + { + fprintf (stderr, "stab_int_type: bad size %u\n", size); + return false; + } + + if (unsignedp) + cache = info->type_cache.signed_integer_types; + else + cache = info->type_cache.unsigned_integer_types; + + if (cache[size - 1] != 0) + return stab_push_defined_type (info, cache[size - 1], size); + else + { + long index; + char buf[100]; + + index = info->type_index; + ++info->type_index; + + cache[size - 1] = index; + + sprintf (buf, "%ld=r%ld;", index, index); + if (unsignedp) + { + strcat (buf, "0;"); + if (size < sizeof (long)) + sprintf (buf + strlen (buf), "%ld;", ((long) 1 << (size * 8)) - 1); + else if (size == sizeof (long)) + strcat (buf, "-1;"); + else if (size == 8) + strcat (buf, "01777777777777777777777;"); + else + abort (); + } + else + { + if (size <= sizeof (long)) + sprintf (buf + strlen (buf), "%ld;%ld;", + (long) - ((unsigned long) 1 << (size * 8 - 1)), + (long) (((unsigned long) 1 << (size * 8 - 1)) - 1)); + else if (size == 8) + strcat (buf, "01000000000000000000000;0777777777777777777777;"); + else + abort (); + } + + return stab_push_string (info, buf, index, true, size); + } +} + +/* Push a floating point type. */ + +static boolean +stab_float_type (p, size) + PTR p; + unsigned int size; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + if (size > 0 + && size - 1 < (sizeof info->type_cache.float_types + / sizeof info->type_cache.float_types[0]) + && info->type_cache.float_types[size - 1] != 0) + return stab_push_defined_type (info, + info->type_cache.float_types[size - 1], + size); + else + { + long index; + char *int_type; + char buf[50]; + + /* Floats are defined as a subrange of int. */ + if (! stab_int_type (info, 4, false)) + return false; + int_type = stab_pop_type (info); + + index = info->type_index; + ++info->type_index; + + if (size > 0 + && size - 1 < (sizeof info->type_cache.float_types + / sizeof info->type_cache.float_types[0])) + info->type_cache.float_types[size - 1] = index; + + sprintf (buf, "%ld=r%s;%u;0;", index, int_type, size); + + free (int_type); + + return stab_push_string (info, buf, index, true, size); + } +} + +/* Push a complex type. */ + +static boolean +stab_complex_type (p, size) + PTR p; + unsigned int size; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char buf[50]; + long index; + + index = info->type_index; + ++info->type_index; + + sprintf (buf, "%ld=r%ld;%u;0;", index, index, size); + + return stab_push_string (info, buf, index, true, size * 2); +} + +/* Push a boolean type. We use an XCOFF predefined type, since gdb + always recognizes them. */ + +static boolean +stab_bool_type (p, size) + PTR p; + unsigned int size; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long index; + + switch (size) + { + case 1: + index = -21; + break; + + case 2: + index = -22; + break; + + default: + case 4: + index = -16; + break; + + case 8: + index = -33; + break; + } + + return stab_push_defined_type (info, index, size); +} + +/* Push an enum type. */ + +static boolean +stab_enum_type (p, tag, names, vals) + PTR p; + const char *tag; + const char **names; + bfd_signed_vma *vals; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + size_t len; + const char **pn; + char *buf; + long index = 0; + bfd_signed_vma *pv; + + if (names == NULL) + { + assert (tag != NULL); + + buf = (char *) xmalloc (10 + strlen (tag)); + sprintf (buf, "xe%s:", tag); + /* FIXME: The size is just a guess. */ + if (! stab_push_string (info, buf, 0, false, 4)) + return false; + free (buf); + return true; + } + + len = 10; + if (tag != NULL) + len += strlen (tag); + for (pn = names; *pn != NULL; pn++) + len += strlen (*pn) + 20; + + if (tag == NULL) + strcpy (buf, "e"); + else + { + index = info->type_index; + ++info->type_index; + sprintf (buf, "%s:T%ld=e", tag, index); + } + + buf = (char *) xmalloc (len); + for (pn = names, pv = vals; *pn != NULL; pn++, pv++) + sprintf (buf + strlen (buf), "%s:%ld,", *pn, (long) *pv); + strcat (buf, ";"); + + if (tag == NULL) + { + /* FIXME: The size is just a guess. */ + if (! stab_push_string (info, buf, 0, false, 4)) + return false; + } + else + { + /* FIXME: The size is just a guess. */ + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf) + || ! stab_push_defined_type (info, index, 4)) + return false; + } + + free (buf); + + return true; +} + +/* Push a modification of the top type on the stack. Cache the + results in CACHE and CACHE_ALLOC. */ + +static boolean +stab_modify_type (info, mod, size, cache, cache_alloc) + struct stab_write_handle *info; + int mod; + unsigned int size; + long **cache; + size_t *cache_alloc; +{ + long targindex; + long index; + char *s, *buf; + + assert (info->type_stack != NULL); + targindex = info->type_stack->index; + + if (targindex <= 0 + || cache == NULL) + { + boolean definition; + + /* Either the target type has no index, or we aren't caching + this modifier. Either way we have no way of recording the + new type, so we don't bother to define one. */ + definition = info->type_stack->definition; + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 2); + sprintf (buf, "%c%s", mod, s); + free (s); + if (! stab_push_string (info, buf, 0, definition, size)) + return false; + free (buf); + } + else + { + if (targindex >= *cache_alloc) + { + size_t alloc; + + alloc = *cache_alloc; + if (alloc == 0) + alloc = 10; + while (targindex >= alloc) + alloc *= 2; + *cache = (long *) xrealloc (*cache, alloc * sizeof (long)); + memset (*cache + *cache_alloc, 0, + (alloc - *cache_alloc) * sizeof (long)); + *cache_alloc = alloc; + } + + index = (*cache)[targindex]; + if (index != 0) + { + /* If we have already defined a modification of this type, + then the entry on the type stack can not be a definition, + so we can safely discard it. */ + assert (! info->type_stack->definition); + free (stab_pop_type (info)); + if (! stab_push_defined_type (info, index, size)) + return false; + } + else + { + index = info->type_index; + ++info->type_index; + + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 20); + sprintf (buf, "%ld=%c%s", index, mod, s); + free (s); + + (*cache)[targindex] = index; + + if (! stab_push_string (info, buf, index, true, size)) + return false; + + free (buf); + } + } + + return true; +} + +/* Push a pointer type. */ + +static boolean +stab_pointer_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* FIXME: The size should depend upon the architecture. */ + return stab_modify_type (info, '*', 4, &info->type_cache.pointer_types, + &info->type_cache.pointer_types_alloc); +} + +/* Push a function type. */ + +static boolean +stab_function_type (p, argcount, varargs) + PTR p; + int argcount; + boolean varargs; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + int i; + + /* We have no way to represent the argument types, so we just + discard them. However, if they define new types, we must output + them. We do this by producing meaningless typedefs. */ + for (i = 0; i < argcount; i++) + { + if (! info->type_stack->definition) + free (stab_pop_type (info)); + else + { + char *s, *buf; + long index; + + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (s) + 40); + index = info->type_index; + ++info->type_index; + sprintf (buf, "__fake_type_%ld:t%s", index, s); + + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return false; + + free (buf); + } + } + + return stab_modify_type (info, 'f', 0, &info->type_cache.function_types, + &info->type_cache.function_types_alloc); +} + +/* Push a reference type. */ + +static boolean +stab_reference_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* FIXME: The size should depend upon the architecture. */ + return stab_modify_type (info, '&', 4, &info->type_cache.reference_types, + &info->type_cache.reference_types_alloc); +} + +/* Push a range type. */ + +static boolean +stab_range_type (p, low, high) + PTR p; + bfd_signed_vma low; + bfd_signed_vma high; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + unsigned int size; + char *s, *buf; + + definition = info->type_stack->definition; + size = info->type_stack->size; + + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 100); + sprintf (buf, "r%s;%ld;%ld;", s, (long) low, (long) high); + free (s); + + if (! stab_push_string (info, buf, 0, definition, size)) + return false; + + free (buf); + + return true; +} + +/* Push an array type. */ + +static boolean +stab_array_type (p, low, high, stringp) + PTR p; + bfd_signed_vma low; + bfd_signed_vma high; + boolean stringp; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + unsigned int element_size; + char *range, *element, *buf; + long index; + unsigned int size; + + definition = info->type_stack->definition; + range = stab_pop_type (info); + + definition = definition || info->type_stack->definition; + element_size = info->type_stack->size; + element = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (range) + strlen (element) + 100); + + if (! stringp) + { + index = 0; + *buf = '\0'; + } + else + { + /* We need to define a type in order to include the string + attribute. */ + index = info->type_index; + ++info->type_index; + definition = true; + sprintf (buf, "%ld=@S;", index); + } + + sprintf (buf + strlen (buf), "ar%s;%ld;%ld;%s", range, low, high, element); + free (range); + free (element); + + if (high <= low) + size = 0; + else + size = element_size * ((high - low) + 1); + if (! stab_push_string (info, buf, index, definition, size)) + return false; + + free (buf); + + return true; +} + +/* Push a set type. */ + +static boolean +stab_set_type (p, bitstringp) + PTR p; + boolean bitstringp; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + char *s, *buf; + long index; + + definition = info->type_stack->definition; + + s = stab_pop_type (info); + buf = (char *) xmalloc (strlen (s) + 30); + + if (! bitstringp) + { + *buf = '\0'; + index = 0; + } + else + { + /* We need to define a type in order to include the string + attribute. */ + index = info->type_index; + ++info->type_index; + definition = true; + sprintf (buf, "%ld=@S;", index); + } + + sprintf (buf + strlen (buf), "S%s", s); + free (s); + + if (! stab_push_string (info, buf, index, definition, 0)) + return false; + + free (buf); + + return true; +} + +/* Push an offset type. */ + +static boolean +stab_offset_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + char *target, *base, *buf; + + definition = info->type_stack->definition; + target = stab_pop_type (info); + + definition = definition || info->type_stack->definition; + base = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (target) + strlen (base) + 3); + sprintf (buf, "@%s,%s", base, target); + free (base); + free (target); + + if (! stab_push_string (info, buf, 0, definition, 0)) + return false; + + free (buf); + + return true; +} + +/* Push a method type. */ + +static boolean +stab_method_type (p, domainp, argcount, varargs) + PTR p; + boolean domainp; + int argcount; + boolean varargs; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + char *domain, *return_type, *buf; + char **args; + int i; + size_t len; + + /* We don't bother with stub method types, because that would + require a mangler for C++ argument types. This will waste space + in the debugging output. */ + + /* We need a domain. I'm not sure DOMAINP can ever be false, + anyhow. */ + if (! domainp) + { + if (! stab_empty_type (p)) + return false; + } + + definition = info->type_stack->definition; + domain = stab_pop_type (info); + + /* A non-varargs function is indicated by making the last parameter + type be void. */ + + if (argcount < 0) + { + args = NULL; + argcount = 0; + } + else if (argcount == 0) + { + if (varargs) + args = NULL; + else + { + args = (char **) xmalloc (1 * sizeof (*args)); + if (! stab_empty_type (p)) + return false; + definition = definition || info->type_stack->definition; + args[0] = stab_pop_type (info); + argcount = 1; + } + } + else + { + args = (char **) xmalloc ((argcount + 1) * sizeof (*args)); + for (i = argcount - 1; i >= 0; i--) + { + definition = definition || info->type_stack->definition; + args[i] = stab_pop_type (info); + } + if (! varargs) + { + if (! stab_empty_type (p)) + return false; + definition = definition || info->type_stack->definition; + args[argcount] = stab_pop_type (info); + ++argcount; + } + } + + definition = definition || info->type_stack->definition; + return_type = stab_pop_type (info); + + len = strlen (domain) + strlen (return_type) + 10; + for (i = 0; i < argcount; i++) + len += strlen (args[i]); + + buf = (char *) xmalloc (len); + + sprintf (buf, "#%s,%s", domain, return_type); + free (domain); + free (return_type); + for (i = 0; i < argcount; i++) + { + strcat (buf, ","); + strcat (buf, args[i]); + free (args[i]); + } + strcat (buf, ";"); + + if (args != NULL) + free (args); + + if (! stab_push_string (info, buf, 0, definition, 0)) + return false; + + free (buf); + + return true; +} + +/* Push a const version of a type. */ + +static boolean +stab_const_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_modify_type (info, 'k', info->type_stack->size, + (long **) NULL, (size_t *) NULL); +} + +/* Push a volatile version of a type. */ + +static boolean +stab_volatile_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_modify_type (info, 'B', info->type_stack->size, + (long **) NULL, (size_t *) NULL); +} + +/* Get the type index to use for a struct/union/class ID. This should + return -1 if it fails. */ + +static long +stab_get_struct_index (info, tag, id, kind, psize) + struct stab_write_handle *info; + const char *tag; + unsigned int id; + enum debug_type_kind kind; + unsigned int *psize; +{ + if (id >= info->type_cache.struct_types_alloc) + { + size_t alloc; + + alloc = info->type_cache.struct_types_alloc; + if (alloc == 0) + alloc = 10; + while (id >= alloc) + alloc *= 2; + info->type_cache.struct_types = + (struct stab_tag *) xrealloc (info->type_cache.struct_types, + alloc * sizeof (struct stab_tag)); + memset ((info->type_cache.struct_types + + info->type_cache.struct_types_alloc), + 0, + ((alloc - info->type_cache.struct_types_alloc) + * sizeof (struct stab_tag))); + info->type_cache.struct_types_alloc = alloc; + } + + if (info->type_cache.struct_types[id].index == 0) + { + info->type_cache.struct_types[id].index = info->type_index; + ++info->type_index; + info->type_cache.struct_types[id].tag = tag; + info->type_cache.struct_types[id].kind = kind; + } + + if (kind == DEBUG_KIND_ILLEGAL) + { + /* This is a definition of the struct. */ + info->type_cache.struct_types[id].kind = kind; + info->type_cache.struct_types[id].size = *psize; + } + else + *psize = info->type_cache.struct_types[id].size; + + return info->type_cache.struct_types[id].index; +} + +/* Start outputting a struct. We ignore the tag, and handle it in + stab_tag. */ + +/*ARGSUSED*/ +static boolean +stab_start_struct_type (p, tag, id, structp, size) + PTR p; + const char *tag; + unsigned int id; + boolean structp; + unsigned int size; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long index; + boolean definition; + char *buf; + + buf = (char *) xmalloc (40); + + if (id == 0) + { + index = 0; + *buf = '\0'; + definition = false; + } + else + { + index = stab_get_struct_index (info, tag, id, DEBUG_KIND_ILLEGAL, + &size); + if (index < 0) + return false; + sprintf (buf, "%ld=", index); + definition = true; + } + + sprintf (buf + strlen (buf), "%c%u", + structp ? 's' : 'u', + size); + + if (! stab_push_string (info, buf, index, definition, size)) + return false; + + info->type_stack->fields = (char *) xmalloc (1); + info->type_stack->fields[0] = '\0'; + + return true; +} + +/* Add a field to a struct. */ + +static boolean +stab_struct_field (p, name, bitpos, bitsize, visibility) + PTR p; + const char *name; + bfd_vma bitpos; + bfd_vma bitsize; + enum debug_visibility visibility; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + unsigned int size; + char *s, *n; + const char *vis; + + definition = info->type_stack->definition; + size = info->type_stack->size; + s = stab_pop_type (info); + + /* Add this field to the end of the current struct fields, which is + currently on the top of the stack. */ + + assert (info->type_stack->fields != NULL); + n = (char *) xmalloc (strlen (info->type_stack->fields) + + strlen (name) + + strlen (s) + + 50); + + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PUBLIC: + vis = ""; + break; + + case DEBUG_VISIBILITY_PRIVATE: + vis = "/0"; + break; + + case DEBUG_VISIBILITY_PROTECTED: + vis = "/1"; + break; + } + + if (bitsize == 0) + { + bitsize = size * 8; + if (bitsize == 0) + fprintf (stderr, + "%s: warning: unknown size for field `%s' in struct\n", + bfd_get_filename (info->abfd), name); + } + + sprintf (n, "%s%s:%s%s,%ld,%ld;", info->type_stack->fields, name, vis, s, + (long) bitpos, (long) bitsize); + + free (info->type_stack->fields); + info->type_stack->fields = n; + + if (definition) + info->type_stack->definition = true; + + return true; +} + +/* Finish up a struct. */ + +static boolean +stab_end_struct_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + long index; + unsigned int size; + char *fields, *first, *buf; + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + definition = info->type_stack->definition; + index = info->type_stack->index; + size = info->type_stack->size; + fields = info->type_stack->fields; + first = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (first) + strlen (fields) + 2); + sprintf (buf, "%s%s;", first, fields); + free (first); + free (fields); + + if (! stab_push_string (info, buf, index, definition, size)) + return false; + + free (buf); + + return true; +} + +/* Start outputting a class. */ + +static boolean +stab_start_class_type (p, tag, id, structp, size, vptr, ownvptr) + PTR p; + const char *tag; + unsigned int id; + boolean structp; + unsigned int size; + boolean vptr; + boolean ownvptr; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + char *vstring; + + if (! vptr || ownvptr) + { + definition = false; + vstring = NULL; + } + else + { + definition = info->type_stack->definition; + vstring = stab_pop_type (info); + } + + if (! stab_start_struct_type (p, tag, id, structp, size)) + return false; + + if (vptr) + { + char *vtable; + + if (ownvptr) + { + assert (info->type_stack->index > 0); + vtable = (char *) xmalloc (20); + sprintf (vtable, "~%%%ld", info->type_stack->index); + } + else + { + vtable = (char *) xmalloc (strlen (vstring) + 3); + sprintf (vtable, "~%%%s", vstring); + free (vstring); + } + + info->type_stack->vtable = vtable; + } + + if (definition) + info->type_stack->definition = true; + + return true; +} + +/* Add a static member to the class on the type stack. */ + +static boolean +stab_class_static_member (p, name, physname, visibility) + PTR p; + const char *name; + const char *physname; + enum debug_visibility visibility; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + char *s, *n; + const char *vis; + + definition = info->type_stack->definition; + s = stab_pop_type (info); + + /* Add this field to the end of the current struct fields, which is + currently on the top of the stack. */ + + assert (info->type_stack->fields != NULL); + n = (char *) xmalloc (strlen (info->type_stack->fields) + + strlen (name) + + strlen (s) + + strlen (physname) + + 10); + + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PUBLIC: + vis = ""; + break; + + case DEBUG_VISIBILITY_PRIVATE: + vis = "/0"; + break; + + case DEBUG_VISIBILITY_PROTECTED: + vis = "/1"; + break; + } + + sprintf (n, "%s%s:%s%s:%s;", info->type_stack->fields, name, vis, s, + physname); + + free (info->type_stack->fields); + info->type_stack->fields = n; + + if (definition) + info->type_stack->definition = true; + + return true; +} + +/* Add a base class to the class on the type stack. */ + +static boolean +stab_class_baseclass (p, bitpos, virtual, visibility) + PTR p; + bfd_vma bitpos; + boolean virtual; + enum debug_visibility visibility; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + boolean definition; + char *s; + char *buf; + unsigned int c; + char **baseclasses; + + definition = info->type_stack->definition; + s = stab_pop_type (info); + + /* Build the base class specifier. */ + + buf = (char *) xmalloc (strlen (s) + 25); + buf[0] = virtual ? '1' : '0'; + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PRIVATE: + buf[1] = '0'; + break; + + case DEBUG_VISIBILITY_PROTECTED: + buf[1] = '1'; + break; + + case DEBUG_VISIBILITY_PUBLIC: + buf[1] = '2'; + break; + } + + sprintf (buf + 2, "%ld,%s;", (long) bitpos, s); + free (s); + + /* Add the new baseclass to the existing ones. */ + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + if (info->type_stack->baseclasses == NULL) + c = 0; + else + { + c = 0; + while (info->type_stack->baseclasses[c] != NULL) + ++c; + } + + baseclasses = (char **) xrealloc (info->type_stack->baseclasses, + (c + 2) * sizeof (*baseclasses)); + baseclasses[c] = buf; + baseclasses[c + 1] = NULL; + + info->type_stack->baseclasses = baseclasses; + + if (definition) + info->type_stack->definition = true; + + return true; +} + +/* Start adding a method to the class on the type stack. */ + +static boolean +stab_class_start_method (p, name) + PTR p; + const char *name; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *m; + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + if (info->type_stack->methods == NULL) + { + m = (char *) xmalloc (strlen (name) + 3); + *m = '\0'; + } + else + { + m = (char *) xrealloc (info->type_stack->methods, + (strlen (info->type_stack->methods) + + strlen (name) + + 4)); + } + + sprintf (m + strlen (m), "%s::", name); + + info->type_stack->methods = m; + + return true; +} + +/* Add a variant, either static or not, to the current method. */ + +static boolean +stab_class_method_var (info, physname, visibility, staticp, constp, volatilep, + voffset, contextp) + struct stab_write_handle *info; + const char *physname; + enum debug_visibility visibility; + boolean staticp; + boolean constp; + boolean volatilep; + bfd_vma voffset; + boolean contextp; +{ + boolean definition; + char *type; + char *context = NULL; + char visc, qualc, typec; + + definition = info->type_stack->definition; + type = stab_pop_type (info); + + if (contextp) + { + definition = definition || info->type_stack->definition; + context = stab_pop_type (info); + } + + assert (info->type_stack != NULL && info->type_stack->methods != NULL); + + switch (visibility) + { + default: + abort (); + + case DEBUG_VISIBILITY_PRIVATE: + visc = '0'; + break; + + case DEBUG_VISIBILITY_PROTECTED: + visc = '1'; + break; + + case DEBUG_VISIBILITY_PUBLIC: + visc = '2'; + break; + } + + if (constp) + { + if (volatilep) + qualc = 'D'; + else + qualc = 'B'; + } + else + { + if (volatilep) + qualc = 'C'; + else + qualc = 'A'; + } + + if (staticp) + typec = '?'; + else if (! contextp) + typec = '.'; + else + typec = '*'; + + info->type_stack->methods = + (char *) xrealloc (info->type_stack->methods, + (strlen (info->type_stack->methods) + + strlen (type) + + strlen (physname) + + (contextp ? strlen (context) : 0) + + 40)); + + sprintf (info->type_stack->methods + strlen (info->type_stack->methods), + "%s:%s;%c%c%c", type, physname, visc, qualc, typec); + free (type); + + if (contextp) + { + sprintf (info->type_stack->methods + strlen (info->type_stack->methods), + "%ld;%s;", (long) voffset, context); + free (context); + } + + if (definition) + info->type_stack->definition = true; + + return true; +} + +/* Add a variant to the current method. */ + +static boolean +stab_class_method_variant (p, physname, visibility, constp, volatilep, + voffset, contextp) + PTR p; + const char *physname; + enum debug_visibility visibility; + boolean constp; + boolean volatilep; + bfd_vma voffset; + boolean contextp; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_class_method_var (info, physname, visibility, false, constp, + volatilep, voffset, contextp); +} + +/* Add a static variant to the current method. */ + +static boolean +stab_class_static_method_variant (p, physname, visibility, constp, volatilep) + PTR p; + const char *physname; + enum debug_visibility visibility; + boolean constp; + boolean volatilep; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + return stab_class_method_var (info, physname, visibility, true, constp, + volatilep, 0, false); +} + +/* Finish up a method. */ + +static boolean +stab_class_end_method (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + assert (info->type_stack != NULL && info->type_stack->methods != NULL); + + /* We allocated enough room on info->type_stack->methods to add the + trailing semicolon. */ + strcat (info->type_stack->methods, ";"); + + return true; +} + +/* Finish up a class. */ + +static boolean +stab_end_class_type (p) + PTR p; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + size_t len; + unsigned int i; + char *buf; + + assert (info->type_stack != NULL && info->type_stack->fields != NULL); + + /* Work out the size we need to allocate for the class definition. */ + + len = (strlen (info->type_stack->string) + + strlen (info->type_stack->fields) + + 10); + if (info->type_stack->baseclasses != NULL) + { + len += 20; + for (i = 0; info->type_stack->baseclasses[i] != NULL; i++) + len += strlen (info->type_stack->baseclasses[i]); + } + if (info->type_stack->methods != NULL) + len += strlen (info->type_stack->methods); + if (info->type_stack->vtable != NULL) + len += strlen (info->type_stack->vtable); + + /* Build the class definition. */ + + buf = (char *) xmalloc (len); + + strcpy (buf, info->type_stack->string); + + if (info->type_stack->baseclasses != NULL) + { + sprintf (buf + strlen (buf), "!%u,", i); + for (i = 0; info->type_stack->baseclasses[i] != NULL; i++) + { + strcat (buf, info->type_stack->baseclasses[i]); + free (info->type_stack->baseclasses[i]); + } + free (info->type_stack->baseclasses); + info->type_stack->baseclasses = NULL; + } + + strcat (buf, info->type_stack->fields); + free (info->type_stack->fields); + info->type_stack->fields = NULL; + + if (info->type_stack->methods != NULL) + { + strcat (buf, info->type_stack->methods); + free (info->type_stack->methods); + info->type_stack->methods = NULL; + } + + strcat (buf, ";"); + + if (info->type_stack->vtable != NULL) + { + strcat (buf, info->type_stack->vtable); + free (info->type_stack->vtable); + info->type_stack->vtable = NULL; + } + + /* Replace the string on the top of the stack with the complete + class definition. */ + free (info->type_stack->string); + info->type_stack->string = buf; + + return true; +} + +/* Push a typedef which was previously defined. */ + +static boolean +stab_typedef_type (p, name) + PTR p; + const char *name; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + struct string_hash_entry *h; + + h = string_hash_lookup (&info->typedef_hash, name, false, false); + assert (h != NULL && h->index > 0); + + return stab_push_defined_type (info, h->index, h->size); +} + +/* Push a struct, union or class tag. */ + +static boolean +stab_tag_type (p, name, id, kind) + PTR p; + const char *name; + unsigned int id; + enum debug_type_kind kind; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long index; + unsigned int size; + + index = stab_get_struct_index (info, name, id, kind, &size); + if (index < 0) + return false; + + return stab_push_defined_type (info, index, size); +} + +/* Define a typedef. */ + +static boolean +stab_typdef (p, name) + PTR p; + const char *name; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + long index; + unsigned int size; + char *s, *buf; + struct string_hash_entry *h; + + index = info->type_stack->index; + size = info->type_stack->size; + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 20); + + if (index > 0) + sprintf (buf, "%s:t%s", name, s); + else + { + index = info->type_index; + ++info->type_index; + sprintf (buf, "%s:t%ld=%s", name, index, s); + } + + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return false; + + free (buf); + + h = string_hash_lookup (&info->typedef_hash, name, true, false); + if (h == NULL) + { + fprintf (stderr, "string_hash_lookup failed: %s\n", + bfd_errmsg (bfd_get_error ())); + return false; + } + + /* I don't think we care about redefinitions. */ + + h->index = index; + h->size = size; + + return true; +} + +/* Define a tag. */ + +static boolean +stab_tag (p, tag) + PTR p; + const char *tag; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (tag) + strlen (s) + 3); + + sprintf (buf, "%s:T%s", tag, s); + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return false; + + free (buf); + + return true; +} + +/* Define an integer constant. */ + +static boolean +stab_int_constant (p, name, val) + PTR p; + const char *name; + bfd_vma val; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *buf; + + buf = (char *) xmalloc (strlen (name) + 20); + sprintf (buf, "%s:c=i%ld", name, (long) val); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return false; + + free (buf); + + return true; +} + +/* Define a floating point constant. */ + +static boolean +stab_float_constant (p, name, val) + PTR p; + const char *name; + double val; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *buf; + + buf = (char *) xmalloc (strlen (name) + 20); + sprintf (buf, "%s:c=f%g", name, val); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return false; + + free (buf); + + return true; +} + +/* Define a typed constant. */ + +static boolean +stab_typed_constant (p, name, val) + PTR p; + const char *name; + bfd_vma val; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + + s = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 20); + sprintf (buf, "%s:c=e%s,%ld", name, s, (long) val); + free (s); + + if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)) + return false; + + free (buf); + + return true; +} + +/* Record a variable. */ + +static boolean +stab_variable (p, name, kind, val) + PTR p; + const char *name; + enum debug_var_kind kind; + bfd_vma val; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + int stab_type; + const char *kindstr; + + s = stab_pop_type (info); + + switch (kind) + { + default: + abort (); + + case DEBUG_GLOBAL: + stab_type = N_GSYM; + kindstr = "G"; + break; + + case DEBUG_STATIC: + stab_type = N_STSYM; + kindstr = "S"; + break; + + case DEBUG_LOCAL_STATIC: + stab_type = N_STSYM; + kindstr = "V"; + break; + + case DEBUG_LOCAL: + stab_type = N_LSYM; + kindstr = ""; + + /* Make sure that this is a type reference or definition. */ + if (! isdigit (*s)) + { + char *n; + long index; + + index = info->type_index; + ++info->type_index; + n = (char *) xmalloc (strlen (s) + 20); + sprintf (n, "%ld=%s", index, s); + free (s); + s = n; + } + break; + + case DEBUG_REGISTER: + stab_type = N_LSYM; + kindstr = "r"; + break; + } + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 3); + sprintf (buf, "%s:%s%s", name, kindstr, s); + free (s); + + if (! stab_write_symbol (info, stab_type, 0, val, buf)) + return false; + + free (buf); + + return true; +} + +/* Start outputting a function. */ + +static boolean +stab_start_function (p, name, globalp) + PTR p; + const char *name; + boolean globalp; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *rettype, *buf; + + assert (info->nesting == 0 && info->fun_offset == -1); + + rettype = stab_pop_type (info); + + buf = (char *) xmalloc (strlen (name) + strlen (rettype) + 3); + sprintf (buf, "%s:%c%s", name, + globalp ? 'F' : 'f', + rettype); + + /* We don't know the value now, so we set it in start_block. */ + info->fun_offset = info->symbols_size; + + if (! stab_write_symbol (info, N_FUN, 0, 0, buf)) + return false; + + free (buf); + + return true; +} + +/* Output a function parameter. */ + +static boolean +stab_function_parameter (p, name, kind, val) + PTR p; + const char *name; + enum debug_parm_kind kind; + bfd_vma val; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + char *s, *buf; + int stab_type; + char kindc; + + s = stab_pop_type (info); + + switch (kind) + { + default: + abort (); + + case DEBUG_PARM_STACK: + stab_type = N_PSYM; + kindc = 'p'; + break; + + case DEBUG_PARM_REG: + stab_type = N_RSYM; + kindc = 'P'; + break; + + case DEBUG_PARM_REFERENCE: + stab_type = N_PSYM; + kindc = 'v'; + break; + + case DEBUG_PARM_REF_REG: + stab_type = N_RSYM; + kindc = 'a'; + break; + } + + buf = (char *) xmalloc (strlen (name) + strlen (s) + 3); + sprintf (buf, "%s:%c%s", name, kindc, s); + free (s); + + if (! stab_write_symbol (info, stab_type, 0, val, buf)) + return false; + + free (buf); + + return true; +} + +/* Start a block. */ + +static boolean +stab_start_block (p, addr) + PTR p; + bfd_vma addr; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + /* Fill in any slots which have been waiting for the first known + text address. */ + + if (info->so_offset != -1) + { + bfd_put_32 (info->abfd, addr, info->symbols + info->so_offset + 8); + info->so_offset = -1; + } + + if (info->fun_offset != -1) + { + bfd_put_32 (info->abfd, addr, info->symbols + info->fun_offset + 8); + info->fun_offset = -1; + } + + ++info->nesting; + + /* We will be called with a top level block surrounding the + function, but stabs information does not output that block, so we + ignore it. */ + + if (info->nesting == 1) + { + info->fnaddr = addr; + return true; + } + + /* We have to output the LBRAC symbol after any variables which are + declared inside the block. We postpone the LBRAC until the next + start_block or end_block. */ + + /* If we have postponed an LBRAC, output it now. */ + if (info->pending_lbrac != (bfd_vma) -1) + { + if (! stab_write_symbol (info, N_LBRAC, 0, info->pending_lbrac, + (const char *) NULL)) + return false; + } + + /* Remember the address and output it later. */ + + info->pending_lbrac = addr - info->fnaddr; + + return true; +} + +/* End a block. */ + +static boolean +stab_end_block (p, addr) + PTR p; + bfd_vma addr; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + if (addr > info->last_text_address) + info->last_text_address = addr; + + /* If we have postponed an LBRAC, output it now. */ + if (info->pending_lbrac != (bfd_vma) -1) + { + if (! stab_write_symbol (info, N_LBRAC, 0, info->pending_lbrac, + (const char *) NULL)) + return false; + info->pending_lbrac = (bfd_vma) -1; + } + + assert (info->nesting > 0); + + --info->nesting; + + /* We ignore the outermost block. */ + if (info->nesting == 0) + return true; + + return stab_write_symbol (info, N_RBRAC, 0, addr - info->fnaddr, + (const char *) NULL); +} + +/* End a function. */ + +/*ARGSUSED*/ +static boolean +stab_end_function (p) + PTR p; +{ + return true; +} + +/* Output a line number. */ + +static boolean +stab_lineno (p, file, lineno, addr) + PTR p; + const char *file; + unsigned long lineno; + bfd_vma addr; +{ + struct stab_write_handle *info = (struct stab_write_handle *) p; + + assert (info->lineno_filename != NULL); + + if (addr > info->last_text_address) + info->last_text_address = addr; + + if (strcmp (file, info->lineno_filename) != 0) + { + if (! stab_write_symbol (info, N_SOL, 0, addr, file)) + return false; + info->lineno_filename = file; + } + + return stab_write_symbol (info, N_SLINE, lineno, addr - info->fnaddr, + (const char *) NULL); +}