mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-21 01:12:32 +08:00
fd67aa1129
Adds two new external authors to etc/update-copyright.py to cover bfd/ax_tls.m4, and adds gprofng to dirs handled automatically, then updates copyright messages as follows: 1) Update cgen/utils.scm emitted copyrights. 2) Run "etc/update-copyright.py --this-year" with an extra external author I haven't committed, 'Kalray SA.', to cover gas testsuite files (which should have their copyright message removed). 3) Build with --enable-maintainer-mode --enable-cgen-maint=yes. 4) Check out */po/*.pot which we don't update frequently.
2236 lines
54 KiB
C
2236 lines
54 KiB
C
/* 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);
|
||
}
|