| /* wrstabs.c -- Output stabs debugging information |
| Copyright (C) 1996-2024 Free Software Foundation, Inc. |
| Written by Ian Lance Taylor <ian@cygnus.com>. |
| |
| 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 3 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., 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| /* This file contains code which writes out stabs debugging |
| information. */ |
| |
| #include "sysdep.h" |
| #include <assert.h> |
| #include "bfd.h" |
| #include "libiberty.h" |
| #include "filenames.h" |
| #include "safe-ctype.h" |
| #include "bucomm.h" |
| #include "debug.h" |
| #include "budbg.h" |
| #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; |
| /* Next string in this table. */ |
| struct string_hash_entry *next; |
| /* 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. */ |
| bool 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 is a list of hash table entries for the strings. */ |
| struct string_hash_entry *strings; |
| /* The last string hash table entry. */ |
| struct string_hash_entry *last_string; |
| /* The size of the strings. */ |
| size_t strings_size; |
| /* 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 bool stab_start_compilation_unit (void *, const char *); |
| static bool stab_start_source (void *, const char *); |
| static bool stab_empty_type (void *); |
| static bool stab_void_type (void *); |
| static bool stab_int_type (void *, unsigned int, bool); |
| static bool stab_float_type (void *, unsigned int); |
| static bool stab_complex_type (void *, unsigned int); |
| static bool stab_bool_type (void *, unsigned int); |
| static bool stab_enum_type |
| (void *, const char *, const char **, bfd_signed_vma *); |
| static bool stab_pointer_type (void *); |
| static bool stab_function_type (void *, int, bool); |
| static bool stab_reference_type (void *); |
| static bool stab_range_type (void *, bfd_signed_vma, bfd_signed_vma); |
| static bool stab_array_type |
| (void *, bfd_signed_vma, bfd_signed_vma, bool); |
| static bool stab_set_type (void *, bool); |
| static bool stab_offset_type (void *); |
| static bool stab_method_type (void *, bool, int, bool); |
| static bool stab_const_type (void *); |
| static bool stab_volatile_type (void *); |
| static bool stab_start_struct_type |
| (void *, const char *, unsigned int, bool, unsigned int); |
| static bool stab_struct_field |
| (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility); |
| static bool stab_end_struct_type (void *); |
| static bool stab_start_class_type |
| (void *, const char *, unsigned int, bool, unsigned int, |
| bool, bool); |
| static bool stab_class_static_member |
| (void *, const char *, const char *, enum debug_visibility); |
| static bool stab_class_baseclass |
| (void *, bfd_vma, bool, enum debug_visibility); |
| static bool stab_class_start_method (void *, const char *); |
| static bool stab_class_method_variant |
| (void *, const char *, enum debug_visibility, bool, bool, |
| bfd_vma, bool); |
| static bool stab_class_static_method_variant |
| (void *, const char *, enum debug_visibility, bool, bool); |
| static bool stab_class_end_method (void *); |
| static bool stab_end_class_type (void *); |
| static bool stab_typedef_type (void *, const char *); |
| static bool stab_tag_type |
| (void *, const char *, unsigned int, enum debug_type_kind); |
| static bool stab_typdef (void *, const char *); |
| static bool stab_tag (void *, const char *); |
| static bool stab_int_constant (void *, const char *, bfd_vma); |
| static bool stab_float_constant (void *, const char *, double); |
| static bool stab_typed_constant (void *, const char *, bfd_vma); |
| static bool stab_variable |
| (void *, const char *, enum debug_var_kind, bfd_vma); |
| static bool stab_start_function (void *, const char *, bool); |
| static bool stab_function_parameter |
| (void *, const char *, enum debug_parm_kind, bfd_vma); |
| static bool stab_start_block (void *, bfd_vma); |
| static bool stab_end_block (void *, bfd_vma); |
| static bool stab_end_function (void *); |
| static bool stab_lineno (void *, 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 (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->next = NULL; |
| 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 bool |
| stab_write_symbol (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, true); |
| if (h == NULL) |
| { |
| non_fatal (_("string_hash_lookup failed: %s"), |
| bfd_errmsg (bfd_get_error ())); |
| return false; |
| } |
| if (h->index != -1) |
| strx = h->index; |
| else |
| { |
| strx = info->strings_size; |
| h->index = strx; |
| if (info->last_string == NULL) |
| info->strings = h; |
| else |
| info->last_string->next = h; |
| info->last_string = h; |
| info->strings_size += strlen (string) + 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 = xrealloc (info->symbols, info->symbols_alloc); |
| } |
| |
| memcpy (info->symbols + info->symbols_size, sym, STAB_SYMBOL_SIZE); |
| |
| info->symbols_size += STAB_SYMBOL_SIZE; |
| |
| return true; |
| } |
| |
| static bool |
| stab_write_symbol_and_free (struct stab_write_handle *info, int type, int desc, |
| bfd_vma value, char *string) |
| { |
| bool ret = stab_write_symbol (info, type, desc, value, string); |
| free (string); |
| return ret; |
| } |
| |
| /* Push a string on to the type stack. */ |
| |
| static bool |
| stab_push_string (struct stab_write_handle *info, char *string, |
| long tindex, bool definition, unsigned int size) |
| { |
| struct stab_type_stack *s; |
| |
| s = xmalloc (sizeof *s); |
| s->string = string; |
| s->index = tindex; |
| 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; |
| } |
| |
| static bool |
| stab_push_string_dup (struct stab_write_handle *info, const char *string, |
| long tindex, bool definition, unsigned int size) |
| { |
| return stab_push_string (info, xstrdup (string), tindex, definition, size); |
| } |
| |
| /* Push a type index which has already been defined. */ |
| |
| static bool |
| stab_push_defined_type (struct stab_write_handle *info, long tindex, |
| unsigned int size) |
| { |
| char buf[20]; |
| |
| sprintf (buf, "%ld", tindex); |
| return stab_push_string_dup (info, buf, tindex, false, size); |
| } |
| |
| /* Pop a type off the type stack. The caller is responsible for |
| freeing the string. */ |
| |
| static char * |
| stab_pop_type (struct stab_write_handle *info) |
| { |
| struct stab_type_stack *s; |
| char *ret; |
| |
| s = info->type_stack; |
| if (s == NULL) |
| return 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. */ |
| |
| bool |
| write_stabs_in_sections_debugging_info (bfd *abfd, void *dhandle, |
| bfd_byte **psyms, |
| bfd_size_type *psymsize, |
| bfd_byte **pstrings, |
| bfd_size_type *pstringsize) |
| { |
| struct stab_write_handle info; |
| struct string_hash_entry *h; |
| bfd_byte *p; |
| bool ret; |
| |
| memset (&info, 0, sizeof info); |
| info.abfd = abfd; |
| |
| info.symbols_alloc = 500; |
| info.symbols = xmalloc (info.symbols_alloc); |
| |
| /* Reserve 1 byte for a null byte. */ |
| info.strings_size = 1; |
| info.type_index = 1; |
| info.so_offset = -1; |
| info.fun_offset = -1; |
| info.pending_lbrac = (bfd_vma) -1; |
| |
| if (!bfd_hash_table_init (&info.strhash.table, string_hash_newfunc, |
| sizeof (struct string_hash_entry)) |
| || !bfd_hash_table_init (&info.typedef_hash.table, string_hash_newfunc, |
| sizeof (struct string_hash_entry))) |
| { |
| non_fatal ("bfd_hash_table_init_failed: %s", |
| bfd_errmsg (bfd_get_error ())); |
| goto fail; |
| } |
| |
| /* The initial symbol holds the string size. */ |
| if (! stab_write_symbol (&info, 0, 0, 0, (const char *) NULL)) |
| goto fail; |
| |
| /* 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))) |
| goto fail; |
| |
| if (! debug_write (dhandle, &stab_fns, (void *) &info)) |
| goto fail; |
| |
| if (info.pending_lbrac != (bfd_vma) -1) |
| goto fail; |
| |
| /* Output a trailing N_SO. */ |
| if (! stab_write_symbol (&info, N_SO, 0, info.last_text_address, |
| (const char *) NULL)) |
| goto fail; |
| |
| /* 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; |
| |
| *pstringsize = info.strings_size; |
| *pstrings = xmalloc (info.strings_size); |
| |
| p = *pstrings; |
| *p++ = '\0'; |
| for (h = info.strings; h != NULL; h = h->next) |
| { |
| strcpy ((char *) p, h->root.string); |
| p += strlen ((char *) p) + 1; |
| } |
| |
| ret = true; |
| goto out; |
| |
| fail: |
| free (info.symbols); |
| ret = false; |
| out: |
| while (info.type_stack != NULL) |
| { |
| struct stab_type_stack *s = info.type_stack; |
| info.type_stack = s->next; |
| free (s->string); |
| free (s->fields); |
| if (s->baseclasses != NULL) |
| { |
| for (int i = 0; s->baseclasses[i] != NULL; i++) |
| free (s->baseclasses[i]); |
| free (s->baseclasses); |
| } |
| free (s->methods); |
| free (s->vtable); |
| free (s); |
| } |
| free (info.type_cache.pointer_types); |
| free (info.type_cache.function_types); |
| free (info.type_cache.reference_types); |
| free (info.type_cache.struct_types); |
| if (info.typedef_hash.table.memory) |
| bfd_hash_table_free (&info.typedef_hash.table); |
| if (info.strhash.table.memory) |
| bfd_hash_table_free (&info.strhash.table); |
| return ret; |
| } |
| |
| /* Start writing out information for a compilation unit. */ |
| |
| static bool |
| stab_start_compilation_unit (void *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 bool |
| stab_start_source (void *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 bool |
| stab_empty_type (void *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 tindex; |
| char buf[40]; |
| |
| tindex = info->type_index; |
| ++info->type_index; |
| |
| sprintf (buf, "%ld=%ld", tindex, tindex); |
| |
| return stab_push_string_dup (info, buf, tindex, false, 0); |
| } |
| } |
| |
| /* Push a void type. */ |
| |
| static bool |
| stab_void_type (void *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 tindex; |
| char buf[40]; |
| |
| tindex = info->type_index; |
| ++info->type_index; |
| |
| info->type_cache.void_type = tindex; |
| |
| sprintf (buf, "%ld=%ld", tindex, tindex); |
| |
| return stab_push_string_dup (info, buf, tindex, true, 0); |
| } |
| } |
| |
| /* Push an integer type. */ |
| |
| static bool |
| stab_int_type (void *p, unsigned int size, bool unsignedp) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| long *cache; |
| |
| if (size <= 0 || (size > sizeof (long) && size != 8)) |
| { |
| non_fatal (_("stab_int_type: bad size %u"), 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 tindex; |
| char buf[100]; |
| |
| tindex = info->type_index; |
| ++info->type_index; |
| |
| cache[size - 1] = tindex; |
| |
| int len = sprintf (buf, "%ld=r%ld;", tindex, tindex); |
| if (unsignedp) |
| { |
| strcpy (buf + len, "0;"); |
| len += 2; |
| if (size < sizeof (long)) |
| sprintf (buf + len, "%ld;", ((long) 1 << (size * 8)) - 1); |
| else if (size == sizeof (long)) |
| strcpy (buf + len, "-1;"); |
| else if (size == 8) |
| strcpy (buf + len, "01777777777777777777777;"); |
| else |
| abort (); |
| } |
| else |
| { |
| if (size <= sizeof (long)) |
| sprintf (buf + len, "%ld;%ld;", |
| (long) - ((unsigned long) 1 << (size * 8 - 1)), |
| (long) (((unsigned long) 1 << (size * 8 - 1)) - 1)); |
| else if (size == 8) |
| strcpy (buf + len, |
| "01000000000000000000000;0777777777777777777777;"); |
| else |
| abort (); |
| } |
| |
| return stab_push_string_dup (info, buf, tindex, true, size); |
| } |
| } |
| |
| /* Push a floating point type. */ |
| |
| static bool |
| stab_float_type (void *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 tindex; |
| 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); |
| |
| tindex = 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] = tindex; |
| |
| sprintf (buf, "%ld=r%s;%u;0;", tindex, int_type, size); |
| |
| free (int_type); |
| |
| return stab_push_string_dup (info, buf, tindex, true, size); |
| } |
| } |
| |
| /* Push a complex type. */ |
| |
| static bool |
| stab_complex_type (void *p, unsigned int size) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| char buf[50]; |
| long tindex; |
| |
| tindex = info->type_index; |
| ++info->type_index; |
| |
| sprintf (buf, "%ld=r%ld;%u;0;", tindex, tindex, size); |
| |
| return stab_push_string_dup (info, buf, tindex, true, size * 2); |
| } |
| |
| /* Push a bool type. We use an XCOFF predefined type, since gdb |
| always recognizes them. */ |
| |
| static bool |
| stab_bool_type (void *p, unsigned int size) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| long tindex; |
| |
| switch (size) |
| { |
| case 1: |
| tindex = -21; |
| break; |
| |
| case 2: |
| tindex = -22; |
| break; |
| |
| default: |
| case 4: |
| tindex = -16; |
| break; |
| |
| case 8: |
| tindex = -33; |
| break; |
| } |
| |
| return stab_push_defined_type (info, tindex, size); |
| } |
| |
| /* Push an enum type. */ |
| |
| static bool |
| stab_enum_type (void *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 tindex = 0; |
| bfd_signed_vma *pv; |
| |
| if (names == NULL) |
| { |
| if (tag == NULL) |
| return false; |
| |
| buf = xmalloc (4 + strlen (tag)); |
| sprintf (buf, "xe%s:", tag); |
| /* FIXME: The size is just a guess. */ |
| return stab_push_string (info, buf, 0, false, 4); |
| } |
| |
| len = 25; |
| if (tag != NULL) |
| len += strlen (tag); |
| for (pn = names; *pn != NULL; pn++) |
| len += strlen (*pn) + 22; |
| |
| buf = xmalloc (len); |
| char *out = buf; |
| if (tag == NULL) |
| out = stpcpy (out, "e"); |
| else |
| { |
| tindex = info->type_index; |
| ++info->type_index; |
| out += sprintf (out, "%s:T%ld=e", tag, tindex); |
| } |
| |
| for (pn = names, pv = vals; *pn != NULL; pn++, pv++) |
| out += sprintf (out, "%s:%ld,", *pn, (long) *pv); |
| strcpy (out, ";"); |
| |
| if (tag == NULL) |
| { |
| /* FIXME: The size is just a guess. */ |
| return stab_push_string (info, buf, 0, false, 4); |
| } |
| else |
| { |
| /* FIXME: The size is just a guess. */ |
| return (stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf) |
| && stab_push_defined_type (info, tindex, 4)); |
| } |
| } |
| |
| /* Push a modification of the top type on the stack. Cache the |
| results in CACHE and CACHE_ALLOC. */ |
| |
| static bool |
| stab_modify_type (struct stab_write_handle *info, int mod, |
| unsigned int size, long **cache, size_t *cache_alloc) |
| { |
| long targindex; |
| long tindex; |
| char *s, *buf; |
| |
| if (info->type_stack == NULL) |
| return false; |
| targindex = info->type_stack->index; |
| |
| if (targindex <= 0 |
| || cache == NULL) |
| { |
| bool 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 = xmalloc (strlen (s) + 2); |
| sprintf (buf, "%c%s", mod, s); |
| free (s); |
| return stab_push_string (info, buf, 0, definition, size); |
| } |
| else |
| { |
| if ((size_t) targindex >= *cache_alloc) |
| { |
| size_t alloc; |
| |
| alloc = *cache_alloc; |
| if (alloc == 0) |
| alloc = 10; |
| while ((size_t) targindex >= alloc) |
| alloc *= 2; |
| *cache = xrealloc (*cache, alloc * sizeof (**cache)); |
| memset (*cache + *cache_alloc, 0, |
| (alloc - *cache_alloc) * sizeof (**cache)); |
| *cache_alloc = alloc; |
| } |
| |
| tindex = (*cache)[targindex]; |
| if (tindex != 0 && ! info->type_stack->definition) |
| { |
| /* We have already defined a modification of this type, and |
| the entry on the type stack is not a definition, so we |
| can safely discard it (we may have a definition on the |
| stack, even if we already defined a modification, if it |
| is a struct which we did not define at the time it was |
| referenced). */ |
| free (stab_pop_type (info)); |
| return stab_push_defined_type (info, tindex, size); |
| } |
| else |
| { |
| tindex = info->type_index; |
| ++info->type_index; |
| |
| s = stab_pop_type (info); |
| buf = xmalloc (strlen (s) + 23); |
| sprintf (buf, "%ld=%c%s", tindex, mod, s); |
| free (s); |
| |
| (*cache)[targindex] = tindex; |
| |
| return stab_push_string (info, buf, tindex, true, size); |
| } |
| } |
| } |
| |
| /* Push a pointer type. */ |
| |
| static bool |
| stab_pointer_type (void *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 bool |
| stab_function_type (void *p, int argcount, |
| bool varargs ATTRIBUTE_UNUSED) |
| { |
| 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 empty typedefs. */ |
| for (i = 0; i < argcount; i++) |
| { |
| if (! info->type_stack->definition) |
| free (stab_pop_type (info)); |
| else |
| { |
| char *s, *buf; |
| |
| s = stab_pop_type (info); |
| |
| buf = xmalloc (strlen (s) + 3); |
| sprintf (buf, ":t%s", s); |
| free (s); |
| |
| return stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf); |
| } |
| } |
| |
| return stab_modify_type (info, 'f', 0, &info->type_cache.function_types, |
| &info->type_cache.function_types_alloc); |
| } |
| |
| /* Push a reference type. */ |
| |
| static bool |
| stab_reference_type (void *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 bool |
| stab_range_type (void *p, bfd_signed_vma low, bfd_signed_vma high) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool definition; |
| unsigned int size; |
| char *s, *buf; |
| |
| definition = info->type_stack->definition; |
| size = info->type_stack->size; |
| |
| s = stab_pop_type (info); |
| buf = xmalloc (strlen (s) + 45); |
| sprintf (buf, "r%s;%ld;%ld;", s, (long) low, (long) high); |
| free (s); |
| |
| return stab_push_string (info, buf, 0, definition, size); |
| } |
| |
| /* Push an array type. */ |
| |
| static bool |
| stab_array_type (void *p, bfd_signed_vma low, bfd_signed_vma high, |
| bool stringp) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool definition; |
| unsigned int element_size; |
| char *range, *element, *buf; |
| long tindex; |
| 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 = xmalloc (strlen (range) + strlen (element) + 70); |
| char *out = buf; |
| if (! stringp) |
| tindex = 0; |
| else |
| { |
| /* We need to define a type in order to include the string |
| attribute. */ |
| tindex = info->type_index; |
| ++info->type_index; |
| definition = true; |
| out += sprintf (out, "%ld=@S;", tindex); |
| } |
| |
| sprintf (out, "ar%s;%ld;%ld;%s", |
| range, (long) low, (long) high, element); |
| free (range); |
| free (element); |
| |
| if (high < low) |
| size = 0; |
| else |
| size = element_size * ((high - low) + 1); |
| return stab_push_string (info, buf, tindex, definition, size); |
| } |
| |
| /* Push a set type. */ |
| |
| static bool |
| stab_set_type (void *p, bool bitstringp) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool definition; |
| char *s, *buf; |
| long tindex; |
| |
| definition = info->type_stack->definition; |
| |
| s = stab_pop_type (info); |
| buf = xmalloc (strlen (s) + 26); |
| char *out = buf; |
| if (! bitstringp) |
| tindex = 0; |
| else |
| { |
| /* We need to define a type in order to include the string |
| attribute. */ |
| tindex = info->type_index; |
| ++info->type_index; |
| definition = true; |
| out += sprintf (out, "%ld=@S;", tindex); |
| } |
| |
| sprintf (out, "S%s", s); |
| free (s); |
| |
| return stab_push_string (info, buf, tindex, definition, 0); |
| } |
| |
| /* Push an offset type. */ |
| |
| static bool |
| stab_offset_type (void *p) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool 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 = xmalloc (strlen (target) + strlen (base) + 3); |
| sprintf (buf, "@%s,%s", base, target); |
| free (base); |
| free (target); |
| |
| return stab_push_string (info, buf, 0, definition, 0); |
| } |
| |
| /* Push a method type. */ |
| |
| static bool |
| stab_method_type (void *p, bool domainp, int argcount, |
| bool varargs) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool 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 = xmalloc (1 * sizeof (*args)); |
| if (! stab_empty_type (p)) |
| { |
| free (args); |
| return false; |
| } |
| definition = definition || info->type_stack->definition; |
| args[0] = stab_pop_type (info); |
| argcount = 1; |
| } |
| } |
| else |
| { |
| args = 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)) |
| { |
| for (i = 0; i < argcount; i++) |
| free (args[i]); |
| free (args); |
| 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) + 4 + argcount; |
| for (i = 0; i < argcount; i++) |
| len += strlen (args[i]); |
| |
| buf = xmalloc (len); |
| char *out = buf; |
| *out++ = '#'; |
| out = stpcpy (out, domain); |
| *out++ = ','; |
| out = stpcpy (out, return_type); |
| free (domain); |
| free (return_type); |
| for (i = 0; i < argcount; i++) |
| { |
| *out++ = ','; |
| out = stpcpy (out, args[i]); |
| free (args[i]); |
| } |
| *out++ = ';'; |
| *out = 0; |
| |
| free (args); |
| |
| return stab_push_string (info, buf, 0, definition, 0); |
| } |
| |
| /* Push a const version of a type. */ |
| |
| static bool |
| stab_const_type (void *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 bool |
| stab_volatile_type (void *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 (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 = |
| xrealloc (info->type_cache.struct_types, |
| alloc * sizeof (*info->type_cache.struct_types)); |
| memset ((info->type_cache.struct_types |
| + info->type_cache.struct_types_alloc), |
| 0, |
| ((alloc - info->type_cache.struct_types_alloc) |
| * sizeof (*info->type_cache.struct_types))); |
| 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. */ |
| |
| static bool |
| stab_start_struct_type (void *p, const char *tag, unsigned int id, |
| bool structp, unsigned int size) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| long tindex; |
| bool definition; |
| char buf[40]; |
| char *out = buf; |
| |
| if (id == 0) |
| { |
| tindex = 0; |
| definition = false; |
| } |
| else |
| { |
| tindex = stab_get_struct_index (info, tag, id, DEBUG_KIND_ILLEGAL, |
| &size); |
| if (tindex < 0) |
| return false; |
| out += sprintf (out, "%ld=", tindex); |
| definition = true; |
| } |
| |
| sprintf (out, "%c%u", |
| structp ? 's' : 'u', |
| size); |
| |
| if (!stab_push_string_dup (info, buf, tindex, definition, size)) |
| return false; |
| |
| info->type_stack->fields = xmalloc (1); |
| info->type_stack->fields[0] = '\0'; |
| |
| return true; |
| } |
| |
| /* Add a field to a struct. */ |
| |
| static bool |
| stab_struct_field (void *p, const char *name, bfd_vma bitpos, |
| bfd_vma bitsize, enum debug_visibility visibility) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool 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. */ |
| if (info->type_stack->fields == NULL) |
| { |
| free (s); |
| return false; |
| } |
| |
| n = 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) |
| non_fatal (_("%s: warning: unknown size for field `%s' in struct"), |
| 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); |
| free (s); |
| info->type_stack->fields = n; |
| |
| if (definition) |
| info->type_stack->definition = true; |
| |
| return true; |
| } |
| |
| /* Finish up a struct. */ |
| |
| static bool |
| stab_end_struct_type (void *p) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool definition; |
| long tindex; |
| unsigned int size; |
| char *fields, *first, *buf; |
| |
| if (info->type_stack == NULL || info->type_stack->fields == NULL) |
| return false; |
| |
| definition = info->type_stack->definition; |
| tindex = info->type_stack->index; |
| size = info->type_stack->size; |
| fields = info->type_stack->fields; |
| first = stab_pop_type (info); |
| |
| buf = xmalloc (strlen (first) + strlen (fields) + 2); |
| sprintf (buf, "%s%s;", first, fields); |
| free (first); |
| free (fields); |
| |
| return stab_push_string (info, buf, tindex, definition, size); |
| } |
| |
| /* Start outputting a class. */ |
| |
| static bool |
| stab_start_class_type (void *p, const char *tag, unsigned int id, |
| bool structp, unsigned int size, |
| bool vptr, bool ownvptr) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool definition = false; |
| char *vstring = NULL; |
| |
| if (vptr && !ownvptr) |
| { |
| definition = info->type_stack->definition; |
| vstring = stab_pop_type (info); |
| } |
| |
| if (! stab_start_struct_type (p, tag, id, structp, size)) |
| { |
| free (vstring); |
| return false; |
| } |
| |
| if (vptr) |
| { |
| char *vtable; |
| |
| if (ownvptr) |
| { |
| if (info->type_stack->index < 1) |
| return false; |
| vtable = xmalloc (23); |
| sprintf (vtable, "~%%%ld", info->type_stack->index); |
| } |
| else |
| { |
| if (vstring == NULL) |
| return false; |
| vtable = xmalloc (strlen (vstring) + 3); |
| sprintf (vtable, "~%%%s", vstring); |
| free (vstring); |
| if (definition) |
| info->type_stack->definition = true; |
| } |
| info->type_stack->vtable = vtable; |
| } |
| |
| return true; |
| } |
| |
| /* Add a static member to the class on the type stack. */ |
| |
| static bool |
| stab_class_static_member (void *p, const char *name, const char *physname, |
| enum debug_visibility visibility) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool 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. */ |
| |
| if (info->type_stack->fields == NULL) |
| return false; |
| n = 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 (s); |
| 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 bool |
| stab_class_baseclass (void *p, bfd_vma bitpos, bool is_virtual, |
| enum debug_visibility visibility) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| bool 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 = xmalloc (strlen (s) + 25); |
| buf[0] = is_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. */ |
| |
| if (info->type_stack == NULL || info->type_stack->fields == NULL) |
| { |
| free (buf); |
| return false; |
| } |
| |
| if (info->type_stack->baseclasses == NULL) |
| c = 0; |
| else |
| { |
| c = 0; |
| while (info->type_stack->baseclasses[c] != NULL) |
| ++c; |
| } |
| |
| baseclasses = 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 bool |
| stab_class_start_method (void *p, const char *name) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| char *m; |
| |
| if (info->type_stack == NULL || info->type_stack->fields == NULL) |
| return false; |
| |
| if (info->type_stack->methods == NULL) |
| { |
| m = xmalloc (strlen (name) + 3); |
| *m = '\0'; |
| } |
| else |
| m = xrealloc (info->type_stack->methods, |
| strlen (info->type_stack->methods) + strlen (name) + 3); |
| |
| 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 bool |
| stab_class_method_var (struct stab_write_handle *info, const char *physname, |
| enum debug_visibility visibility, |
| bool staticp, bool constp, |
| bool volatilep, bfd_vma voffset, |
| bool contextp) |
| { |
| bool 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); |
| } |
| |
| if (info->type_stack == NULL || info->type_stack->methods == NULL) |
| { |
| free (type); |
| free (context); |
| return false; |
| } |
| |
| 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 = '*'; |
| |
| size_t cur_len = strlen (info->type_stack->methods); |
| info->type_stack->methods = |
| xrealloc (info->type_stack->methods, (cur_len |
| + strlen (type) |
| + strlen (physname) |
| + (contextp ? strlen (context) : 0) |
| + 40)); |
| |
| char *out = info->type_stack->methods + cur_len; |
| out += sprintf (out, "%s:%s;%c%c%c", type, physname, visc, qualc, typec); |
| free (type); |
| |
| if (contextp) |
| { |
| sprintf (out, "%ld;%s;", (long) voffset, context); |
| free (context); |
| } |
| |
| if (definition) |
| info->type_stack->definition = true; |
| |
| return true; |
| } |
| |
| /* Add a variant to the current method. */ |
| |
| static bool |
| stab_class_method_variant (void *p, const char *physname, |
| enum debug_visibility visibility, |
| bool constp, bool volatilep, |
| bfd_vma voffset, bool 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 bool |
| stab_class_static_method_variant (void *p, const char *physname, |
| enum debug_visibility visibility, |
| bool constp, bool 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 bool |
| stab_class_end_method (void *p) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| |
| if (info->type_stack == NULL || info->type_stack->methods == NULL) |
| return false; |
| |
| /* 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 bool |
| stab_end_class_type (void *p) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| size_t len; |
| unsigned int i = 0; |
| char *buf; |
| |
| if (info->type_stack == NULL |
| || info->type_stack->string == NULL |
| || info->type_stack->fields == NULL) |
| return false; |
| |
| /* 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 = xmalloc (len); |
| |
| char *out = stpcpy (buf, info->type_stack->string); |
| |
| if (info->type_stack->baseclasses != NULL) |
| { |
| out += sprintf (out, "!%u,", i); |
| for (i = 0; info->type_stack->baseclasses[i] != NULL; i++) |
| { |
| out = stpcpy (out, info->type_stack->baseclasses[i]); |
| free (info->type_stack->baseclasses[i]); |
| } |
| free (info->type_stack->baseclasses); |
| info->type_stack->baseclasses = NULL; |
| } |
| |
| out = stpcpy (out, info->type_stack->fields); |
| free (info->type_stack->fields); |
| info->type_stack->fields = NULL; |
| |
| if (info->type_stack->methods != NULL) |
| { |
| out = stpcpy (out, info->type_stack->methods); |
| free (info->type_stack->methods); |
| info->type_stack->methods = NULL; |
| } |
| |
| out = stpcpy (out, ";"); |
| |
| if (info->type_stack->vtable != NULL) |
| { |
| out = stpcpy (out, 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 bool |
| stab_typedef_type (void *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); |
| if (h == NULL || h->index < 1) |
| return false; |
| |
| return stab_push_defined_type (info, h->index, h->size); |
| } |
| |
| /* Push a struct, union or class tag. */ |
| |
| static bool |
| stab_tag_type (void *p, const char *name, unsigned int id, |
| enum debug_type_kind kind) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| long tindex; |
| unsigned int size = 0; |
| |
| tindex = stab_get_struct_index (info, name, id, kind, &size); |
| if (tindex < 0) |
| return false; |
| |
| return stab_push_defined_type (info, tindex, size); |
| } |
| |
| /* Define a typedef. */ |
| |
| static bool |
| stab_typdef (void *p, const char *name) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| long tindex; |
| unsigned int size; |
| char *s, *buf; |
| struct string_hash_entry *h; |
| |
| tindex = info->type_stack->index; |
| size = info->type_stack->size; |
| s = stab_pop_type (info); |
| |
| buf = xmalloc (strlen (name) + strlen (s) + 20); |
| |
| if (tindex > 0) |
| sprintf (buf, "%s:t%s", name, s); |
| else |
| { |
| tindex = info->type_index; |
| ++info->type_index; |
| sprintf (buf, "%s:t%ld=%s", name, tindex, s); |
| } |
| |
| free (s); |
| |
| if (!stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf)) |
| return false; |
| |
| h = string_hash_lookup (&info->typedef_hash, name, true, false); |
| if (h == NULL) |
| { |
| non_fatal (_("string_hash_lookup failed: %s"), |
| bfd_errmsg (bfd_get_error ())); |
| return false; |
| } |
| |
| /* I don't think we care about redefinitions. */ |
| |
| h->index = tindex; |
| h->size = size; |
| |
| return true; |
| } |
| |
| /* Define a tag. */ |
| |
| static bool |
| stab_tag (void *p, const char *tag) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| char *s, *buf; |
| |
| s = stab_pop_type (info); |
| |
| buf = xmalloc (strlen (tag) + strlen (s) + 3); |
| |
| sprintf (buf, "%s:T%s", tag, s); |
| free (s); |
| |
| return stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf); |
| } |
| |
| /* Define an integer constant. */ |
| |
| static bool |
| stab_int_constant (void *p, const char *name, bfd_vma val) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| char *buf; |
| |
| buf = xmalloc (strlen (name) + 20); |
| sprintf (buf, "%s:c=i%ld", name, (long) val); |
| |
| return stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf); |
| } |
| |
| /* Define a floating point constant. */ |
| |
| static bool |
| stab_float_constant (void *p, const char *name, double val) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| char *buf; |
| |
| buf = xmalloc (strlen (name) + 20); |
| sprintf (buf, "%s:c=f%g", name, val); |
| |
| return stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf); |
| } |
| |
| /* Define a typed constant. */ |
| |
| static bool |
| stab_typed_constant (void *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 = xmalloc (strlen (name) + strlen (s) + 20); |
| sprintf (buf, "%s:c=e%s,%ld", name, s, (long) val); |
| free (s); |
| |
| return stab_write_symbol_and_free (info, N_LSYM, 0, 0, buf); |
| } |
| |
| /* Record a variable. */ |
| |
| static bool |
| stab_variable (void *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 tindex; |
| |
| tindex = info->type_index; |
| ++info->type_index; |
| n = xmalloc (strlen (s) + 20); |
| sprintf (n, "%ld=%s", tindex, s); |
| free (s); |
| s = n; |
| } |
| break; |
| |
| case DEBUG_REGISTER: |
| stab_type = N_RSYM; |
| kindstr = "r"; |
| break; |
| } |
| |
| buf = xmalloc (strlen (name) + strlen (s) + 3); |
| sprintf (buf, "%s:%s%s", name, kindstr, s); |
| free (s); |
| |
| return stab_write_symbol_and_free (info, stab_type, 0, val, buf); |
| } |
| |
| /* Start outputting a function. */ |
| |
| static bool |
| stab_start_function (void *p, const char *name, bool globalp) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| char *rettype, *buf; |
| |
| if (info->nesting != 0 || info->fun_offset != -1) |
| return false; |
| |
| rettype = stab_pop_type (info); |
| |
| buf = xmalloc (strlen (name) + strlen (rettype) + 3); |
| sprintf (buf, "%s:%c%s", name, |
| globalp ? 'F' : 'f', |
| rettype); |
| free (rettype); |
| |
| /* We don't know the value now, so we set it in start_block. */ |
| info->fun_offset = info->symbols_size; |
| |
| return stab_write_symbol_and_free (info, N_FUN, 0, 0, buf); |
| } |
| |
| /* Output a function parameter. */ |
| |
| static bool |
| stab_function_parameter (void *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 = xmalloc (strlen (name) + strlen (s) + 3); |
| sprintf (buf, "%s:%c%s", name, kindc, s); |
| free (s); |
| |
| return stab_write_symbol_and_free (info, stab_type, 0, val, buf); |
| } |
| |
| /* Start a block. */ |
| |
| static bool |
| stab_start_block (void *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 bool |
| stab_end_block (void *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; |
| } |
| |
| if (info->nesting < 1) |
| return false; |
| |
| --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. */ |
| |
| static bool |
| stab_end_function (void *p ATTRIBUTE_UNUSED) |
| { |
| return true; |
| } |
| |
| /* Output a line number. */ |
| |
| static bool |
| stab_lineno (void *p, const char *file, unsigned long lineno, bfd_vma addr) |
| { |
| struct stab_write_handle *info = (struct stab_write_handle *) p; |
| |
| if (info->lineno_filename == NULL) |
| return false; |
| |
| if (addr > info->last_text_address) |
| info->last_text_address = addr; |
| |
| if (filename_cmp (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); |
| } |