2417 lines
56 KiB
C
2417 lines
56 KiB
C
|
/* wrstabs.c -- Output stabs debugging information
|
|||
|
Copyright (C) 1996 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 2 of the License, or
|
|||
|
(at your option) any later version.
|
|||
|
|
|||
|
This program is distributed in the hope that it will be useful,
|
|||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
GNU General Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with this program; if not, write to the Free Software
|
|||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|||
|
02111-1307, USA. */
|
|||
|
|
|||
|
/* This file contains code which writes out stabs debugging
|
|||
|
information. */
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
#include <ctype.h>
|
|||
|
#include <assert.h>
|
|||
|
|
|||
|
#include "bfd.h"
|
|||
|
#include "bucomm.h"
|
|||
|
#include "libiberty.h"
|
|||
|
#include "debug.h"
|
|||
|
#include "budbg.h"
|
|||
|
|
|||
|
/* Meaningless definition needs by aout64.h. FIXME. */
|
|||
|
#define BYTES_IN_WORD 4
|
|||
|
|
|||
|
#include "aout/aout64.h"
|
|||
|
#include "aout/stab_gnu.h"
|
|||
|
|
|||
|
/* The size of a stabs symbol. This presumes 32 bit values. */
|
|||
|
|
|||
|
#define STAB_SYMBOL_SIZE (12)
|
|||
|
|
|||
|
/* An entry in a string hash table. */
|
|||
|
|
|||
|
struct string_hash_entry
|
|||
|
{
|
|||
|
struct bfd_hash_entry root;
|
|||
|
/* 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. */
|
|||
|
boolean definition;
|
|||
|
/* String defining struct fields. */
|
|||
|
char *fields;
|
|||
|
/* NULL terminated array of strings defining base classes for a
|
|||
|
class. */
|
|||
|
char **baseclasses;
|
|||
|
/* String defining class methods. */
|
|||
|
char *methods;
|
|||
|
/* String defining vtable pointer for a class. */
|
|||
|
char *vtable;
|
|||
|
};
|
|||
|
|
|||
|
/* This structure is used to keep track of type indices for tagged
|
|||
|
types. */
|
|||
|
|
|||
|
struct stab_tag
|
|||
|
{
|
|||
|
/* The type index. */
|
|||
|
long index;
|
|||
|
/* The tag name. */
|
|||
|
const char *tag;
|
|||
|
/* The kind of type. This is set to DEBUG_KIND_ILLEGAL when the
|
|||
|
type is defined. */
|
|||
|
enum debug_type_kind kind;
|
|||
|
/* The size of the struct. */
|
|||
|
unsigned int size;
|
|||
|
};
|
|||
|
|
|||
|
/* We remember various sorts of type indices. They are not related,
|
|||
|
but, for convenience, we keep all the information in this
|
|||
|
structure. */
|
|||
|
|
|||
|
struct stab_type_cache
|
|||
|
{
|
|||
|
/* The void type index. */
|
|||
|
long void_type;
|
|||
|
/* Signed integer type indices, indexed by size - 1. */
|
|||
|
long signed_integer_types[8];
|
|||
|
/* Unsigned integer type indices, indexed by size - 1. */
|
|||
|
long unsigned_integer_types[8];
|
|||
|
/* Floating point types, indexed by size - 1. */
|
|||
|
long float_types[16];
|
|||
|
/* Pointers to types, indexed by the type index. */
|
|||
|
long *pointer_types;
|
|||
|
size_t pointer_types_alloc;
|
|||
|
/* Functions returning types, indexed by the type index. */
|
|||
|
long *function_types;
|
|||
|
size_t function_types_alloc;
|
|||
|
/* References to types, indexed by the type index. */
|
|||
|
long *reference_types;
|
|||
|
size_t reference_types_alloc;
|
|||
|
/* Struct/union/class type indices, indexed by the struct id. */
|
|||
|
struct stab_tag *struct_types;
|
|||
|
size_t struct_types_alloc;
|
|||
|
};
|
|||
|
|
|||
|
/* This is the handle passed through debug_write. */
|
|||
|
|
|||
|
struct stab_write_handle
|
|||
|
{
|
|||
|
/* The BFD. */
|
|||
|
bfd *abfd;
|
|||
|
/* This buffer holds the symbols. */
|
|||
|
bfd_byte *symbols;
|
|||
|
size_t symbols_size;
|
|||
|
size_t symbols_alloc;
|
|||
|
/* This 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 struct bfd_hash_entry *string_hash_newfunc
|
|||
|
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
|
|||
|
static boolean stab_write_symbol
|
|||
|
PARAMS ((struct stab_write_handle *, int, int, bfd_vma, const char *));
|
|||
|
static boolean stab_push_string
|
|||
|
PARAMS ((struct stab_write_handle *, const char *, long, boolean,
|
|||
|
unsigned int));
|
|||
|
static boolean stab_push_defined_type
|
|||
|
PARAMS ((struct stab_write_handle *, long, unsigned int));
|
|||
|
static char *stab_pop_type PARAMS ((struct stab_write_handle *));
|
|||
|
static boolean stab_modify_type
|
|||
|
PARAMS ((struct stab_write_handle *, int, unsigned int, long **, size_t *));
|
|||
|
static long stab_get_struct_index
|
|||
|
PARAMS ((struct stab_write_handle *, const char *, unsigned int,
|
|||
|
enum debug_type_kind, unsigned int *));
|
|||
|
static boolean stab_class_method_var
|
|||
|
PARAMS ((struct stab_write_handle *, const char *, enum debug_visibility,
|
|||
|
boolean, boolean, boolean, bfd_vma, boolean));
|
|||
|
|
|||
|
static boolean stab_start_compilation_unit PARAMS ((PTR, const char *));
|
|||
|
static boolean stab_start_source PARAMS ((PTR, const char *));
|
|||
|
static boolean stab_empty_type PARAMS ((PTR));
|
|||
|
static boolean stab_void_type PARAMS ((PTR));
|
|||
|
static boolean stab_int_type PARAMS ((PTR, unsigned int, boolean));
|
|||
|
static boolean stab_float_type PARAMS ((PTR, unsigned int));
|
|||
|
static boolean stab_complex_type PARAMS ((PTR, unsigned int));
|
|||
|
static boolean stab_bool_type PARAMS ((PTR, unsigned int));
|
|||
|
static boolean stab_enum_type
|
|||
|
PARAMS ((PTR, const char *, const char **, bfd_signed_vma *));
|
|||
|
static boolean stab_pointer_type PARAMS ((PTR));
|
|||
|
static boolean stab_function_type PARAMS ((PTR, int, boolean));
|
|||
|
static boolean stab_reference_type PARAMS ((PTR));
|
|||
|
static boolean stab_range_type PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma));
|
|||
|
static boolean stab_array_type
|
|||
|
PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma, boolean));
|
|||
|
static boolean stab_set_type PARAMS ((PTR, boolean));
|
|||
|
static boolean stab_offset_type PARAMS ((PTR));
|
|||
|
static boolean stab_method_type PARAMS ((PTR, boolean, int, boolean));
|
|||
|
static boolean stab_const_type PARAMS ((PTR));
|
|||
|
static boolean stab_volatile_type PARAMS ((PTR));
|
|||
|
static boolean stab_start_struct_type
|
|||
|
PARAMS ((PTR, const char *, unsigned int, boolean, unsigned int));
|
|||
|
static boolean stab_struct_field
|
|||
|
PARAMS ((PTR, const char *, bfd_vma, bfd_vma, enum debug_visibility));
|
|||
|
static boolean stab_end_struct_type PARAMS ((PTR));
|
|||
|
static boolean stab_start_class_type
|
|||
|
PARAMS ((PTR, const char *, unsigned int, boolean, unsigned int, boolean,
|
|||
|
boolean));
|
|||
|
static boolean stab_class_static_member
|
|||
|
PARAMS ((PTR, const char *, const char *, enum debug_visibility));
|
|||
|
static boolean stab_class_baseclass
|
|||
|
PARAMS ((PTR, bfd_vma, boolean, enum debug_visibility));
|
|||
|
static boolean stab_class_start_method PARAMS ((PTR, const char *));
|
|||
|
static boolean stab_class_method_variant
|
|||
|
PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean,
|
|||
|
bfd_vma, boolean));
|
|||
|
static boolean stab_class_static_method_variant
|
|||
|
PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean));
|
|||
|
static boolean stab_class_end_method PARAMS ((PTR));
|
|||
|
static boolean stab_end_class_type PARAMS ((PTR));
|
|||
|
static boolean stab_typedef_type PARAMS ((PTR, const char *));
|
|||
|
static boolean stab_tag_type
|
|||
|
PARAMS ((PTR, const char *, unsigned int, enum debug_type_kind));
|
|||
|
static boolean stab_typdef PARAMS ((PTR, const char *));
|
|||
|
static boolean stab_tag PARAMS ((PTR, const char *));
|
|||
|
static boolean stab_int_constant PARAMS ((PTR, const char *, bfd_vma));
|
|||
|
static boolean stab_float_constant PARAMS ((PTR, const char *, double));
|
|||
|
static boolean stab_typed_constant PARAMS ((PTR, const char *, bfd_vma));
|
|||
|
static boolean stab_variable
|
|||
|
PARAMS ((PTR, const char *, enum debug_var_kind, bfd_vma));
|
|||
|
static boolean stab_start_function PARAMS ((PTR, const char *, boolean));
|
|||
|
static boolean stab_function_parameter
|
|||
|
PARAMS ((PTR, const char *, enum debug_parm_kind, bfd_vma));
|
|||
|
static boolean stab_start_block PARAMS ((PTR, bfd_vma));
|
|||
|
static boolean stab_end_block PARAMS ((PTR, bfd_vma));
|
|||
|
static boolean stab_end_function PARAMS ((PTR));
|
|||
|
static boolean stab_lineno
|
|||
|
PARAMS ((PTR, const char *, unsigned long, bfd_vma));
|
|||
|
|
|||
|
static const struct debug_write_fns stab_fns =
|
|||
|
{
|
|||
|
stab_start_compilation_unit,
|
|||
|
stab_start_source,
|
|||
|
stab_empty_type,
|
|||
|
stab_void_type,
|
|||
|
stab_int_type,
|
|||
|
stab_float_type,
|
|||
|
stab_complex_type,
|
|||
|
stab_bool_type,
|
|||
|
stab_enum_type,
|
|||
|
stab_pointer_type,
|
|||
|
stab_function_type,
|
|||
|
stab_reference_type,
|
|||
|
stab_range_type,
|
|||
|
stab_array_type,
|
|||
|
stab_set_type,
|
|||
|
stab_offset_type,
|
|||
|
stab_method_type,
|
|||
|
stab_const_type,
|
|||
|
stab_volatile_type,
|
|||
|
stab_start_struct_type,
|
|||
|
stab_struct_field,
|
|||
|
stab_end_struct_type,
|
|||
|
stab_start_class_type,
|
|||
|
stab_class_static_member,
|
|||
|
stab_class_baseclass,
|
|||
|
stab_class_start_method,
|
|||
|
stab_class_method_variant,
|
|||
|
stab_class_static_method_variant,
|
|||
|
stab_class_end_method,
|
|||
|
stab_end_class_type,
|
|||
|
stab_typedef_type,
|
|||
|
stab_tag_type,
|
|||
|
stab_typdef,
|
|||
|
stab_tag,
|
|||
|
stab_int_constant,
|
|||
|
stab_float_constant,
|
|||
|
stab_typed_constant,
|
|||
|
stab_variable,
|
|||
|
stab_start_function,
|
|||
|
stab_function_parameter,
|
|||
|
stab_start_block,
|
|||
|
stab_end_block,
|
|||
|
stab_end_function,
|
|||
|
stab_lineno
|
|||
|
};
|
|||
|
|
|||
|
/* Routine to create an entry in a string hash table. */
|
|||
|
|
|||
|
static struct bfd_hash_entry *
|
|||
|
string_hash_newfunc (entry, table, string)
|
|||
|
struct bfd_hash_entry *entry;
|
|||
|
struct bfd_hash_table *table;
|
|||
|
const char *string;
|
|||
|
{
|
|||
|
struct string_hash_entry *ret = (struct string_hash_entry *) entry;
|
|||
|
|
|||
|
/* Allocate the structure if it has not already been allocated by a
|
|||
|
subclass. */
|
|||
|
if (ret == (struct string_hash_entry *) NULL)
|
|||
|
ret = ((struct string_hash_entry *)
|
|||
|
bfd_hash_allocate (table, sizeof (struct string_hash_entry)));
|
|||
|
if (ret == (struct string_hash_entry *) NULL)
|
|||
|
return NULL;
|
|||
|
|
|||
|
/* Call the allocation method of the superclass. */
|
|||
|
ret = ((struct string_hash_entry *)
|
|||
|
bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
|
|||
|
|
|||
|
if (ret)
|
|||
|
{
|
|||
|
/* Initialize the local fields. */
|
|||
|
ret->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 boolean
|
|||
|
stab_write_symbol (info, type, desc, value, string)
|
|||
|
struct stab_write_handle *info;
|
|||
|
int type;
|
|||
|
int desc;
|
|||
|
bfd_vma value;
|
|||
|
const char *string;
|
|||
|
{
|
|||
|
bfd_size_type strx;
|
|||
|
bfd_byte sym[STAB_SYMBOL_SIZE];
|
|||
|
|
|||
|
if (string == NULL)
|
|||
|
strx = 0;
|
|||
|
else
|
|||
|
{
|
|||
|
struct string_hash_entry *h;
|
|||
|
|
|||
|
h = string_hash_lookup (&info->strhash, string, true, true);
|
|||
|
if (h == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "string_hash_lookup failed: %s\n",
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (h->index != -1)
|
|||
|
strx = h->index;
|
|||
|
else
|
|||
|
{
|
|||
|
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 = (bfd_byte *) xrealloc (info->symbols,
|
|||
|
info->symbols_alloc);
|
|||
|
}
|
|||
|
|
|||
|
memcpy (info->symbols + info->symbols_size, sym, STAB_SYMBOL_SIZE);
|
|||
|
|
|||
|
info->symbols_size += STAB_SYMBOL_SIZE;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a string on to the type stack. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_push_string (info, string, index, definition, size)
|
|||
|
struct stab_write_handle *info;
|
|||
|
const char *string;
|
|||
|
long index;
|
|||
|
boolean definition;
|
|||
|
unsigned int size;
|
|||
|
{
|
|||
|
struct stab_type_stack *s;
|
|||
|
|
|||
|
s = (struct stab_type_stack *) xmalloc (sizeof *s);
|
|||
|
s->string = xstrdup (string);
|
|||
|
s->index = index;
|
|||
|
s->definition = definition;
|
|||
|
s->size = size;
|
|||
|
|
|||
|
s->fields = NULL;
|
|||
|
s->baseclasses = NULL;
|
|||
|
s->methods = NULL;
|
|||
|
s->vtable = NULL;
|
|||
|
|
|||
|
s->next = info->type_stack;
|
|||
|
info->type_stack = s;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a type index which has already been defined. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_push_defined_type (info, index, size)
|
|||
|
struct stab_write_handle *info;
|
|||
|
long index;
|
|||
|
unsigned int size;
|
|||
|
{
|
|||
|
char buf[20];
|
|||
|
|
|||
|
sprintf (buf, "%ld", index);
|
|||
|
return stab_push_string (info, buf, index, false, size);
|
|||
|
}
|
|||
|
|
|||
|
/* Pop a type off the type stack. The caller is responsible for
|
|||
|
freeing the string. */
|
|||
|
|
|||
|
static char *
|
|||
|
stab_pop_type (info)
|
|||
|
struct stab_write_handle *info;
|
|||
|
{
|
|||
|
struct stab_type_stack *s;
|
|||
|
char *ret;
|
|||
|
|
|||
|
s = info->type_stack;
|
|||
|
assert (s != NULL);
|
|||
|
|
|||
|
info->type_stack = s->next;
|
|||
|
|
|||
|
ret = s->string;
|
|||
|
|
|||
|
free (s);
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/* The general routine to write out stabs in sections debugging
|
|||
|
information. This accumulates the stabs symbols and the strings in
|
|||
|
two obstacks. We can't easily write out the information as we go
|
|||
|
along, because we need to know the section sizes before we can
|
|||
|
write out the section contents. ABFD is the BFD and DHANDLE is the
|
|||
|
handle for the debugging information. This sets *PSYMS to point to
|
|||
|
the symbols, *PSYMSIZE the size of the symbols, *PSTRINGS to the
|
|||
|
strings, and *PSTRINGSIZE to the size of the strings. */
|
|||
|
|
|||
|
boolean
|
|||
|
write_stabs_in_sections_debugging_info (abfd, dhandle, psyms, psymsize,
|
|||
|
pstrings, pstringsize)
|
|||
|
bfd *abfd;
|
|||
|
PTR dhandle;
|
|||
|
bfd_byte **psyms;
|
|||
|
bfd_size_type *psymsize;
|
|||
|
bfd_byte **pstrings;
|
|||
|
bfd_size_type *pstringsize;
|
|||
|
{
|
|||
|
struct stab_write_handle info;
|
|||
|
struct string_hash_entry *h;
|
|||
|
bfd_byte *p;
|
|||
|
|
|||
|
info.abfd = abfd;
|
|||
|
|
|||
|
info.symbols_size = 0;
|
|||
|
info.symbols_alloc = 500;
|
|||
|
info.symbols = (bfd_byte *) xmalloc (info.symbols_alloc);
|
|||
|
|
|||
|
info.strings = NULL;
|
|||
|
info.last_string = NULL;
|
|||
|
/* Reserve 1 byte for a null byte. */
|
|||
|
info.strings_size = 1;
|
|||
|
|
|||
|
if (! bfd_hash_table_init (&info.strhash.table, string_hash_newfunc)
|
|||
|
|| ! bfd_hash_table_init (&info.typedef_hash.table, string_hash_newfunc))
|
|||
|
{
|
|||
|
fprintf (stderr, "bfd_hash_table_init_failed: %s\n",
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
info.type_stack = NULL;
|
|||
|
info.type_index = 1;
|
|||
|
memset (&info.type_cache, 0, sizeof info.type_cache);
|
|||
|
info.so_offset = -1;
|
|||
|
info.fun_offset = -1;
|
|||
|
info.last_text_address = 0;
|
|||
|
info.nesting = 0;
|
|||
|
info.fnaddr = 0;
|
|||
|
info.pending_lbrac = (bfd_vma) -1;
|
|||
|
|
|||
|
/* The initial symbol holds the string size. */
|
|||
|
if (! stab_write_symbol (&info, 0, 0, 0, (const char *) NULL))
|
|||
|
return false;
|
|||
|
|
|||
|
/* Output an initial N_SO symbol. */
|
|||
|
info.so_offset = info.symbols_size;
|
|||
|
if (! stab_write_symbol (&info, N_SO, 0, 0, bfd_get_filename (abfd)))
|
|||
|
return false;
|
|||
|
|
|||
|
if (! debug_write (dhandle, &stab_fns, (PTR) &info))
|
|||
|
return false;
|
|||
|
|
|||
|
assert (info.pending_lbrac == (bfd_vma) -1);
|
|||
|
|
|||
|
/* Output a trailing N_SO. */
|
|||
|
if (! stab_write_symbol (&info, N_SO, 0, info.last_text_address,
|
|||
|
(const char *) NULL))
|
|||
|
return false;
|
|||
|
|
|||
|
/* Put the string size in the initial symbol. */
|
|||
|
bfd_put_32 (abfd, info.strings_size, info.symbols + 8);
|
|||
|
|
|||
|
*psyms = info.symbols;
|
|||
|
*psymsize = info.symbols_size;
|
|||
|
|
|||
|
*pstringsize = info.strings_size;
|
|||
|
*pstrings = (bfd_byte *) xmalloc (info.strings_size);
|
|||
|
|
|||
|
p = *pstrings;
|
|||
|
*p++ = '\0';
|
|||
|
for (h = info.strings; h != NULL; h = h->next)
|
|||
|
{
|
|||
|
strcpy (p, h->root.string);
|
|||
|
p += strlen (p) + 1;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Start writing out information for a compilation unit. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_start_compilation_unit (p, filename)
|
|||
|
PTR p;
|
|||
|
const char *filename;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
/* We would normally output an N_SO symbol here. However, that
|
|||
|
would force us to reset all of our type information. I think we
|
|||
|
will be better off just outputting an N_SOL symbol, and not
|
|||
|
worrying about splitting information between files. */
|
|||
|
|
|||
|
info->lineno_filename = filename;
|
|||
|
|
|||
|
return stab_write_symbol (info, N_SOL, 0, 0, filename);
|
|||
|
}
|
|||
|
|
|||
|
/* Start writing out information for a particular source file. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_start_source (p, filename)
|
|||
|
PTR p;
|
|||
|
const char *filename;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
/* FIXME: The symbol's value is supposed to be the text section
|
|||
|
address. However, we would have to fill it in later, and gdb
|
|||
|
doesn't care, so we don't bother with it. */
|
|||
|
|
|||
|
info->lineno_filename = filename;
|
|||
|
|
|||
|
return stab_write_symbol (info, N_SOL, 0, 0, filename);
|
|||
|
}
|
|||
|
|
|||
|
/* Push an empty type. This shouldn't normally happen. We just use a
|
|||
|
void type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_empty_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
/* We don't call stab_void_type if the type is not yet defined,
|
|||
|
because that might screw up the typedef. */
|
|||
|
|
|||
|
if (info->type_cache.void_type != 0)
|
|||
|
return stab_push_defined_type (info, info->type_cache.void_type, 0);
|
|||
|
else
|
|||
|
{
|
|||
|
long index;
|
|||
|
char buf[40];
|
|||
|
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
|
|||
|
sprintf (buf, "%ld=%ld", index, index);
|
|||
|
|
|||
|
return stab_push_string (info, buf, index, false, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Push a void type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_void_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
if (info->type_cache.void_type != 0)
|
|||
|
return stab_push_defined_type (info, info->type_cache.void_type, 0);
|
|||
|
else
|
|||
|
{
|
|||
|
long index;
|
|||
|
char buf[40];
|
|||
|
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
|
|||
|
info->type_cache.void_type = index;
|
|||
|
|
|||
|
sprintf (buf, "%ld=%ld", index, index);
|
|||
|
|
|||
|
return stab_push_string (info, buf, index, true, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Push an integer type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_int_type (p, size, unsignedp)
|
|||
|
PTR p;
|
|||
|
unsigned int size;
|
|||
|
boolean unsignedp;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
long *cache;
|
|||
|
|
|||
|
if (size <= 0 || (size > sizeof (long) && size != 8))
|
|||
|
{
|
|||
|
fprintf (stderr, "stab_int_type: bad size %u\n", size);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
if (unsignedp)
|
|||
|
cache = info->type_cache.signed_integer_types;
|
|||
|
else
|
|||
|
cache = info->type_cache.unsigned_integer_types;
|
|||
|
|
|||
|
if (cache[size - 1] != 0)
|
|||
|
return stab_push_defined_type (info, cache[size - 1], size);
|
|||
|
else
|
|||
|
{
|
|||
|
long index;
|
|||
|
char buf[100];
|
|||
|
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
|
|||
|
cache[size - 1] = index;
|
|||
|
|
|||
|
sprintf (buf, "%ld=r%ld;", index, index);
|
|||
|
if (unsignedp)
|
|||
|
{
|
|||
|
strcat (buf, "0;");
|
|||
|
if (size < sizeof (long))
|
|||
|
sprintf (buf + strlen (buf), "%ld;", ((long) 1 << (size * 8)) - 1);
|
|||
|
else if (size == sizeof (long))
|
|||
|
strcat (buf, "-1;");
|
|||
|
else if (size == 8)
|
|||
|
strcat (buf, "01777777777777777777777;");
|
|||
|
else
|
|||
|
abort ();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (size <= sizeof (long))
|
|||
|
sprintf (buf + strlen (buf), "%ld;%ld;",
|
|||
|
(long) - ((unsigned long) 1 << (size * 8 - 1)),
|
|||
|
(long) (((unsigned long) 1 << (size * 8 - 1)) - 1));
|
|||
|
else if (size == 8)
|
|||
|
strcat (buf, "01000000000000000000000;0777777777777777777777;");
|
|||
|
else
|
|||
|
abort ();
|
|||
|
}
|
|||
|
|
|||
|
return stab_push_string (info, buf, index, true, size);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Push a floating point type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_float_type (p, size)
|
|||
|
PTR p;
|
|||
|
unsigned int size;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
if (size > 0
|
|||
|
&& size - 1 < (sizeof info->type_cache.float_types
|
|||
|
/ sizeof info->type_cache.float_types[0])
|
|||
|
&& info->type_cache.float_types[size - 1] != 0)
|
|||
|
return stab_push_defined_type (info,
|
|||
|
info->type_cache.float_types[size - 1],
|
|||
|
size);
|
|||
|
else
|
|||
|
{
|
|||
|
long index;
|
|||
|
char *int_type;
|
|||
|
char buf[50];
|
|||
|
|
|||
|
/* Floats are defined as a subrange of int. */
|
|||
|
if (! stab_int_type (info, 4, false))
|
|||
|
return false;
|
|||
|
int_type = stab_pop_type (info);
|
|||
|
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
|
|||
|
if (size > 0
|
|||
|
&& size - 1 < (sizeof info->type_cache.float_types
|
|||
|
/ sizeof info->type_cache.float_types[0]))
|
|||
|
info->type_cache.float_types[size - 1] = index;
|
|||
|
|
|||
|
sprintf (buf, "%ld=r%s;%u;0;", index, int_type, size);
|
|||
|
|
|||
|
free (int_type);
|
|||
|
|
|||
|
return stab_push_string (info, buf, index, true, size);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Push a complex type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_complex_type (p, size)
|
|||
|
PTR p;
|
|||
|
unsigned int size;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char buf[50];
|
|||
|
long index;
|
|||
|
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
|
|||
|
sprintf (buf, "%ld=r%ld;%u;0;", index, index, size);
|
|||
|
|
|||
|
return stab_push_string (info, buf, index, true, size * 2);
|
|||
|
}
|
|||
|
|
|||
|
/* Push a boolean type. We use an XCOFF predefined type, since gdb
|
|||
|
always recognizes them. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_bool_type (p, size)
|
|||
|
PTR p;
|
|||
|
unsigned int size;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
long index;
|
|||
|
|
|||
|
switch (size)
|
|||
|
{
|
|||
|
case 1:
|
|||
|
index = -21;
|
|||
|
break;
|
|||
|
|
|||
|
case 2:
|
|||
|
index = -22;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
case 4:
|
|||
|
index = -16;
|
|||
|
break;
|
|||
|
|
|||
|
case 8:
|
|||
|
index = -33;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return stab_push_defined_type (info, index, size);
|
|||
|
}
|
|||
|
|
|||
|
/* Push an enum type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_enum_type (p, tag, names, vals)
|
|||
|
PTR p;
|
|||
|
const char *tag;
|
|||
|
const char **names;
|
|||
|
bfd_signed_vma *vals;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
size_t len;
|
|||
|
const char **pn;
|
|||
|
char *buf;
|
|||
|
long index = 0;
|
|||
|
bfd_signed_vma *pv;
|
|||
|
|
|||
|
if (names == NULL)
|
|||
|
{
|
|||
|
assert (tag != NULL);
|
|||
|
|
|||
|
buf = (char *) xmalloc (10 + strlen (tag));
|
|||
|
sprintf (buf, "xe%s:", tag);
|
|||
|
/* FIXME: The size is just a guess. */
|
|||
|
if (! stab_push_string (info, buf, 0, false, 4))
|
|||
|
return false;
|
|||
|
free (buf);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
len = 10;
|
|||
|
if (tag != NULL)
|
|||
|
len += strlen (tag);
|
|||
|
for (pn = names; *pn != NULL; pn++)
|
|||
|
len += strlen (*pn) + 20;
|
|||
|
|
|||
|
buf = (char *) xmalloc (len);
|
|||
|
|
|||
|
if (tag == NULL)
|
|||
|
strcpy (buf, "e");
|
|||
|
else
|
|||
|
{
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
sprintf (buf, "%s:T%ld=e", tag, index);
|
|||
|
}
|
|||
|
|
|||
|
for (pn = names, pv = vals; *pn != NULL; pn++, pv++)
|
|||
|
sprintf (buf + strlen (buf), "%s:%ld,", *pn, (long) *pv);
|
|||
|
strcat (buf, ";");
|
|||
|
|
|||
|
if (tag == NULL)
|
|||
|
{
|
|||
|
/* FIXME: The size is just a guess. */
|
|||
|
if (! stab_push_string (info, buf, 0, false, 4))
|
|||
|
return false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* FIXME: The size is just a guess. */
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf)
|
|||
|
|| ! stab_push_defined_type (info, index, 4))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a modification of the top type on the stack. Cache the
|
|||
|
results in CACHE and CACHE_ALLOC. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_modify_type (info, mod, size, cache, cache_alloc)
|
|||
|
struct stab_write_handle *info;
|
|||
|
int mod;
|
|||
|
unsigned int size;
|
|||
|
long **cache;
|
|||
|
size_t *cache_alloc;
|
|||
|
{
|
|||
|
long targindex;
|
|||
|
long index;
|
|||
|
char *s, *buf;
|
|||
|
|
|||
|
assert (info->type_stack != NULL);
|
|||
|
targindex = info->type_stack->index;
|
|||
|
|
|||
|
if (targindex <= 0
|
|||
|
|| cache == NULL)
|
|||
|
{
|
|||
|
boolean definition;
|
|||
|
|
|||
|
/* Either the target type has no index, or we aren't caching
|
|||
|
this modifier. Either way we have no way of recording the
|
|||
|
new type, so we don't bother to define one. */
|
|||
|
definition = info->type_stack->definition;
|
|||
|
s = stab_pop_type (info);
|
|||
|
buf = (char *) xmalloc (strlen (s) + 2);
|
|||
|
sprintf (buf, "%c%s", mod, s);
|
|||
|
free (s);
|
|||
|
if (! stab_push_string (info, buf, 0, definition, size))
|
|||
|
return false;
|
|||
|
free (buf);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (targindex >= *cache_alloc)
|
|||
|
{
|
|||
|
size_t alloc;
|
|||
|
|
|||
|
alloc = *cache_alloc;
|
|||
|
if (alloc == 0)
|
|||
|
alloc = 10;
|
|||
|
while (targindex >= alloc)
|
|||
|
alloc *= 2;
|
|||
|
*cache = (long *) xrealloc (*cache, alloc * sizeof (long));
|
|||
|
memset (*cache + *cache_alloc, 0,
|
|||
|
(alloc - *cache_alloc) * sizeof (long));
|
|||
|
*cache_alloc = alloc;
|
|||
|
}
|
|||
|
|
|||
|
index = (*cache)[targindex];
|
|||
|
if (index != 0 && ! 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));
|
|||
|
if (! stab_push_defined_type (info, index, size))
|
|||
|
return false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
buf = (char *) xmalloc (strlen (s) + 20);
|
|||
|
sprintf (buf, "%ld=%c%s", index, mod, s);
|
|||
|
free (s);
|
|||
|
|
|||
|
(*cache)[targindex] = index;
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, index, true, size))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a pointer type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_pointer_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
/* FIXME: The size should depend upon the architecture. */
|
|||
|
return stab_modify_type (info, '*', 4, &info->type_cache.pointer_types,
|
|||
|
&info->type_cache.pointer_types_alloc);
|
|||
|
}
|
|||
|
|
|||
|
/* Push a function type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_function_type (p, argcount, varargs)
|
|||
|
PTR p;
|
|||
|
int argcount;
|
|||
|
boolean varargs;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
int i;
|
|||
|
|
|||
|
/* We have no way to represent the argument types, so we just
|
|||
|
discard them. However, if they define new types, we must output
|
|||
|
them. We do this by producing 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 = (char *) xmalloc (strlen (s) + 3);
|
|||
|
sprintf (buf, ":t%s", s);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return stab_modify_type (info, 'f', 0, &info->type_cache.function_types,
|
|||
|
&info->type_cache.function_types_alloc);
|
|||
|
}
|
|||
|
|
|||
|
/* Push a reference type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_reference_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
/* FIXME: The size should depend upon the architecture. */
|
|||
|
return stab_modify_type (info, '&', 4, &info->type_cache.reference_types,
|
|||
|
&info->type_cache.reference_types_alloc);
|
|||
|
}
|
|||
|
|
|||
|
/* Push a range type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_range_type (p, low, high)
|
|||
|
PTR p;
|
|||
|
bfd_signed_vma low;
|
|||
|
bfd_signed_vma high;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
unsigned int size;
|
|||
|
char *s, *buf;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
size = info->type_stack->size;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
buf = (char *) xmalloc (strlen (s) + 100);
|
|||
|
sprintf (buf, "r%s;%ld;%ld;", s, (long) low, (long) high);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, 0, definition, size))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push an array type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_array_type (p, low, high, stringp)
|
|||
|
PTR p;
|
|||
|
bfd_signed_vma low;
|
|||
|
bfd_signed_vma high;
|
|||
|
boolean stringp;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
unsigned int element_size;
|
|||
|
char *range, *element, *buf;
|
|||
|
long index;
|
|||
|
unsigned int size;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
range = stab_pop_type (info);
|
|||
|
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
element_size = info->type_stack->size;
|
|||
|
element = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (range) + strlen (element) + 100);
|
|||
|
|
|||
|
if (! stringp)
|
|||
|
{
|
|||
|
index = 0;
|
|||
|
*buf = '\0';
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* We need to define a type in order to include the string
|
|||
|
attribute. */
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
definition = true;
|
|||
|
sprintf (buf, "%ld=@S;", index);
|
|||
|
}
|
|||
|
|
|||
|
sprintf (buf + strlen (buf), "ar%s;%ld;%ld;%s",
|
|||
|
range, (long) low, (long) high, element);
|
|||
|
free (range);
|
|||
|
free (element);
|
|||
|
|
|||
|
if (high < low)
|
|||
|
size = 0;
|
|||
|
else
|
|||
|
size = element_size * ((high - low) + 1);
|
|||
|
if (! stab_push_string (info, buf, index, definition, size))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a set type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_set_type (p, bitstringp)
|
|||
|
PTR p;
|
|||
|
boolean bitstringp;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
char *s, *buf;
|
|||
|
long index;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
buf = (char *) xmalloc (strlen (s) + 30);
|
|||
|
|
|||
|
if (! bitstringp)
|
|||
|
{
|
|||
|
*buf = '\0';
|
|||
|
index = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* We need to define a type in order to include the string
|
|||
|
attribute. */
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
definition = true;
|
|||
|
sprintf (buf, "%ld=@S;", index);
|
|||
|
}
|
|||
|
|
|||
|
sprintf (buf + strlen (buf), "S%s", s);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, index, definition, 0))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push an offset type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_offset_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
char *target, *base, *buf;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
target = stab_pop_type (info);
|
|||
|
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
base = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (target) + strlen (base) + 3);
|
|||
|
sprintf (buf, "@%s,%s", base, target);
|
|||
|
free (base);
|
|||
|
free (target);
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, 0, definition, 0))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a method type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_method_type (p, domainp, argcount, varargs)
|
|||
|
PTR p;
|
|||
|
boolean domainp;
|
|||
|
int argcount;
|
|||
|
boolean varargs;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
char *domain, *return_type, *buf;
|
|||
|
char **args;
|
|||
|
int i;
|
|||
|
size_t len;
|
|||
|
|
|||
|
/* We don't bother with stub method types, because that would
|
|||
|
require a mangler for C++ argument types. This will waste space
|
|||
|
in the debugging output. */
|
|||
|
|
|||
|
/* We need a domain. I'm not sure DOMAINP can ever be false,
|
|||
|
anyhow. */
|
|||
|
if (! domainp)
|
|||
|
{
|
|||
|
if (! stab_empty_type (p))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
domain = stab_pop_type (info);
|
|||
|
|
|||
|
/* A non-varargs function is indicated by making the last parameter
|
|||
|
type be void. */
|
|||
|
|
|||
|
if (argcount < 0)
|
|||
|
{
|
|||
|
args = NULL;
|
|||
|
argcount = 0;
|
|||
|
}
|
|||
|
else if (argcount == 0)
|
|||
|
{
|
|||
|
if (varargs)
|
|||
|
args = NULL;
|
|||
|
else
|
|||
|
{
|
|||
|
args = (char **) xmalloc (1 * sizeof (*args));
|
|||
|
if (! stab_empty_type (p))
|
|||
|
return false;
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
args[0] = stab_pop_type (info);
|
|||
|
argcount = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
args = (char **) xmalloc ((argcount + 1) * sizeof (*args));
|
|||
|
for (i = argcount - 1; i >= 0; i--)
|
|||
|
{
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
args[i] = stab_pop_type (info);
|
|||
|
}
|
|||
|
if (! varargs)
|
|||
|
{
|
|||
|
if (! stab_empty_type (p))
|
|||
|
return false;
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
args[argcount] = stab_pop_type (info);
|
|||
|
++argcount;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
return_type = stab_pop_type (info);
|
|||
|
|
|||
|
len = strlen (domain) + strlen (return_type) + 10;
|
|||
|
for (i = 0; i < argcount; i++)
|
|||
|
len += strlen (args[i]);
|
|||
|
|
|||
|
buf = (char *) xmalloc (len);
|
|||
|
|
|||
|
sprintf (buf, "#%s,%s", domain, return_type);
|
|||
|
free (domain);
|
|||
|
free (return_type);
|
|||
|
for (i = 0; i < argcount; i++)
|
|||
|
{
|
|||
|
strcat (buf, ",");
|
|||
|
strcat (buf, args[i]);
|
|||
|
free (args[i]);
|
|||
|
}
|
|||
|
strcat (buf, ";");
|
|||
|
|
|||
|
if (args != NULL)
|
|||
|
free (args);
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, 0, definition, 0))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a const version of a type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_const_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
return stab_modify_type (info, 'k', info->type_stack->size,
|
|||
|
(long **) NULL, (size_t *) NULL);
|
|||
|
}
|
|||
|
|
|||
|
/* Push a volatile version of a type. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_volatile_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
return stab_modify_type (info, 'B', info->type_stack->size,
|
|||
|
(long **) NULL, (size_t *) NULL);
|
|||
|
}
|
|||
|
|
|||
|
/* Get the type index to use for a struct/union/class ID. This should
|
|||
|
return -1 if it fails. */
|
|||
|
|
|||
|
static long
|
|||
|
stab_get_struct_index (info, tag, id, kind, psize)
|
|||
|
struct stab_write_handle *info;
|
|||
|
const char *tag;
|
|||
|
unsigned int id;
|
|||
|
enum debug_type_kind kind;
|
|||
|
unsigned int *psize;
|
|||
|
{
|
|||
|
if (id >= info->type_cache.struct_types_alloc)
|
|||
|
{
|
|||
|
size_t alloc;
|
|||
|
|
|||
|
alloc = info->type_cache.struct_types_alloc;
|
|||
|
if (alloc == 0)
|
|||
|
alloc = 10;
|
|||
|
while (id >= alloc)
|
|||
|
alloc *= 2;
|
|||
|
info->type_cache.struct_types =
|
|||
|
(struct stab_tag *) xrealloc (info->type_cache.struct_types,
|
|||
|
alloc * sizeof (struct stab_tag));
|
|||
|
memset ((info->type_cache.struct_types
|
|||
|
+ info->type_cache.struct_types_alloc),
|
|||
|
0,
|
|||
|
((alloc - info->type_cache.struct_types_alloc)
|
|||
|
* sizeof (struct stab_tag)));
|
|||
|
info->type_cache.struct_types_alloc = alloc;
|
|||
|
}
|
|||
|
|
|||
|
if (info->type_cache.struct_types[id].index == 0)
|
|||
|
{
|
|||
|
info->type_cache.struct_types[id].index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
info->type_cache.struct_types[id].tag = tag;
|
|||
|
info->type_cache.struct_types[id].kind = kind;
|
|||
|
}
|
|||
|
|
|||
|
if (kind == DEBUG_KIND_ILLEGAL)
|
|||
|
{
|
|||
|
/* This is a definition of the struct. */
|
|||
|
info->type_cache.struct_types[id].kind = kind;
|
|||
|
info->type_cache.struct_types[id].size = *psize;
|
|||
|
}
|
|||
|
else
|
|||
|
*psize = info->type_cache.struct_types[id].size;
|
|||
|
|
|||
|
return info->type_cache.struct_types[id].index;
|
|||
|
}
|
|||
|
|
|||
|
/* Start outputting a struct. We ignore the tag, and handle it in
|
|||
|
stab_tag. */
|
|||
|
|
|||
|
/*ARGSUSED*/
|
|||
|
static boolean
|
|||
|
stab_start_struct_type (p, tag, id, structp, size)
|
|||
|
PTR p;
|
|||
|
const char *tag;
|
|||
|
unsigned int id;
|
|||
|
boolean structp;
|
|||
|
unsigned int size;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
long index;
|
|||
|
boolean definition;
|
|||
|
char *buf;
|
|||
|
|
|||
|
buf = (char *) xmalloc (40);
|
|||
|
|
|||
|
if (id == 0)
|
|||
|
{
|
|||
|
index = 0;
|
|||
|
*buf = '\0';
|
|||
|
definition = false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
index = stab_get_struct_index (info, tag, id, DEBUG_KIND_ILLEGAL,
|
|||
|
&size);
|
|||
|
if (index < 0)
|
|||
|
return false;
|
|||
|
sprintf (buf, "%ld=", index);
|
|||
|
definition = true;
|
|||
|
}
|
|||
|
|
|||
|
sprintf (buf + strlen (buf), "%c%u",
|
|||
|
structp ? 's' : 'u',
|
|||
|
size);
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, index, definition, size))
|
|||
|
return false;
|
|||
|
|
|||
|
info->type_stack->fields = (char *) xmalloc (1);
|
|||
|
info->type_stack->fields[0] = '\0';
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Add a field to a struct. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_struct_field (p, name, bitpos, bitsize, visibility)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
bfd_vma bitpos;
|
|||
|
bfd_vma bitsize;
|
|||
|
enum debug_visibility visibility;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
unsigned int size;
|
|||
|
char *s, *n;
|
|||
|
const char *vis;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
size = info->type_stack->size;
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
/* Add this field to the end of the current struct fields, which is
|
|||
|
currently on the top of the stack. */
|
|||
|
|
|||
|
assert (info->type_stack->fields != NULL);
|
|||
|
n = (char *) xmalloc (strlen (info->type_stack->fields)
|
|||
|
+ strlen (name)
|
|||
|
+ strlen (s)
|
|||
|
+ 50);
|
|||
|
|
|||
|
switch (visibility)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PUBLIC:
|
|||
|
vis = "";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PRIVATE:
|
|||
|
vis = "/0";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PROTECTED:
|
|||
|
vis = "/1";
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (bitsize == 0)
|
|||
|
{
|
|||
|
bitsize = size * 8;
|
|||
|
if (bitsize == 0)
|
|||
|
fprintf (stderr,
|
|||
|
"%s: warning: unknown size for field `%s' in struct\n",
|
|||
|
bfd_get_filename (info->abfd), name);
|
|||
|
}
|
|||
|
|
|||
|
sprintf (n, "%s%s:%s%s,%ld,%ld;", info->type_stack->fields, name, vis, s,
|
|||
|
(long) bitpos, (long) bitsize);
|
|||
|
|
|||
|
free (info->type_stack->fields);
|
|||
|
info->type_stack->fields = n;
|
|||
|
|
|||
|
if (definition)
|
|||
|
info->type_stack->definition = true;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Finish up a struct. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_end_struct_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
long index;
|
|||
|
unsigned int size;
|
|||
|
char *fields, *first, *buf;
|
|||
|
|
|||
|
assert (info->type_stack != NULL && info->type_stack->fields != NULL);
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
index = info->type_stack->index;
|
|||
|
size = info->type_stack->size;
|
|||
|
fields = info->type_stack->fields;
|
|||
|
first = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (first) + strlen (fields) + 2);
|
|||
|
sprintf (buf, "%s%s;", first, fields);
|
|||
|
free (first);
|
|||
|
free (fields);
|
|||
|
|
|||
|
if (! stab_push_string (info, buf, index, definition, size))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Start outputting a class. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_start_class_type (p, tag, id, structp, size, vptr, ownvptr)
|
|||
|
PTR p;
|
|||
|
const char *tag;
|
|||
|
unsigned int id;
|
|||
|
boolean structp;
|
|||
|
unsigned int size;
|
|||
|
boolean vptr;
|
|||
|
boolean ownvptr;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
char *vstring;
|
|||
|
|
|||
|
if (! vptr || ownvptr)
|
|||
|
{
|
|||
|
definition = false;
|
|||
|
vstring = NULL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
definition = info->type_stack->definition;
|
|||
|
vstring = stab_pop_type (info);
|
|||
|
}
|
|||
|
|
|||
|
if (! stab_start_struct_type (p, tag, id, structp, size))
|
|||
|
return false;
|
|||
|
|
|||
|
if (vptr)
|
|||
|
{
|
|||
|
char *vtable;
|
|||
|
|
|||
|
if (ownvptr)
|
|||
|
{
|
|||
|
assert (info->type_stack->index > 0);
|
|||
|
vtable = (char *) xmalloc (20);
|
|||
|
sprintf (vtable, "~%%%ld", info->type_stack->index);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
vtable = (char *) xmalloc (strlen (vstring) + 3);
|
|||
|
sprintf (vtable, "~%%%s", vstring);
|
|||
|
free (vstring);
|
|||
|
}
|
|||
|
|
|||
|
info->type_stack->vtable = vtable;
|
|||
|
}
|
|||
|
|
|||
|
if (definition)
|
|||
|
info->type_stack->definition = true;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Add a static member to the class on the type stack. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_static_member (p, name, physname, visibility)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
const char *physname;
|
|||
|
enum debug_visibility visibility;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
char *s, *n;
|
|||
|
const char *vis;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
/* Add this field to the end of the current struct fields, which is
|
|||
|
currently on the top of the stack. */
|
|||
|
|
|||
|
assert (info->type_stack->fields != NULL);
|
|||
|
n = (char *) xmalloc (strlen (info->type_stack->fields)
|
|||
|
+ strlen (name)
|
|||
|
+ strlen (s)
|
|||
|
+ strlen (physname)
|
|||
|
+ 10);
|
|||
|
|
|||
|
switch (visibility)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PUBLIC:
|
|||
|
vis = "";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PRIVATE:
|
|||
|
vis = "/0";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PROTECTED:
|
|||
|
vis = "/1";
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
sprintf (n, "%s%s:%s%s:%s;", info->type_stack->fields, name, vis, s,
|
|||
|
physname);
|
|||
|
|
|||
|
free (info->type_stack->fields);
|
|||
|
info->type_stack->fields = n;
|
|||
|
|
|||
|
if (definition)
|
|||
|
info->type_stack->definition = true;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Add a base class to the class on the type stack. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_baseclass (p, bitpos, virtual, visibility)
|
|||
|
PTR p;
|
|||
|
bfd_vma bitpos;
|
|||
|
boolean virtual;
|
|||
|
enum debug_visibility visibility;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
boolean definition;
|
|||
|
char *s;
|
|||
|
char *buf;
|
|||
|
unsigned int c;
|
|||
|
char **baseclasses;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
/* Build the base class specifier. */
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (s) + 25);
|
|||
|
buf[0] = virtual ? '1' : '0';
|
|||
|
switch (visibility)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PRIVATE:
|
|||
|
buf[1] = '0';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PROTECTED:
|
|||
|
buf[1] = '1';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PUBLIC:
|
|||
|
buf[1] = '2';
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
sprintf (buf + 2, "%ld,%s;", (long) bitpos, s);
|
|||
|
free (s);
|
|||
|
|
|||
|
/* Add the new baseclass to the existing ones. */
|
|||
|
|
|||
|
assert (info->type_stack != NULL && info->type_stack->fields != NULL);
|
|||
|
|
|||
|
if (info->type_stack->baseclasses == NULL)
|
|||
|
c = 0;
|
|||
|
else
|
|||
|
{
|
|||
|
c = 0;
|
|||
|
while (info->type_stack->baseclasses[c] != NULL)
|
|||
|
++c;
|
|||
|
}
|
|||
|
|
|||
|
baseclasses = (char **) xrealloc (info->type_stack->baseclasses,
|
|||
|
(c + 2) * sizeof (*baseclasses));
|
|||
|
baseclasses[c] = buf;
|
|||
|
baseclasses[c + 1] = NULL;
|
|||
|
|
|||
|
info->type_stack->baseclasses = baseclasses;
|
|||
|
|
|||
|
if (definition)
|
|||
|
info->type_stack->definition = true;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Start adding a method to the class on the type stack. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_start_method (p, name)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *m;
|
|||
|
|
|||
|
assert (info->type_stack != NULL && info->type_stack->fields != NULL);
|
|||
|
|
|||
|
if (info->type_stack->methods == NULL)
|
|||
|
{
|
|||
|
m = (char *) xmalloc (strlen (name) + 3);
|
|||
|
*m = '\0';
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m = (char *) xrealloc (info->type_stack->methods,
|
|||
|
(strlen (info->type_stack->methods)
|
|||
|
+ strlen (name)
|
|||
|
+ 4));
|
|||
|
}
|
|||
|
|
|||
|
sprintf (m + strlen (m), "%s::", name);
|
|||
|
|
|||
|
info->type_stack->methods = m;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Add a variant, either static or not, to the current method. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_method_var (info, physname, visibility, staticp, constp, volatilep,
|
|||
|
voffset, contextp)
|
|||
|
struct stab_write_handle *info;
|
|||
|
const char *physname;
|
|||
|
enum debug_visibility visibility;
|
|||
|
boolean staticp;
|
|||
|
boolean constp;
|
|||
|
boolean volatilep;
|
|||
|
bfd_vma voffset;
|
|||
|
boolean contextp;
|
|||
|
{
|
|||
|
boolean definition;
|
|||
|
char *type;
|
|||
|
char *context = NULL;
|
|||
|
char visc, qualc, typec;
|
|||
|
|
|||
|
definition = info->type_stack->definition;
|
|||
|
type = stab_pop_type (info);
|
|||
|
|
|||
|
if (contextp)
|
|||
|
{
|
|||
|
definition = definition || info->type_stack->definition;
|
|||
|
context = stab_pop_type (info);
|
|||
|
}
|
|||
|
|
|||
|
assert (info->type_stack != NULL && info->type_stack->methods != NULL);
|
|||
|
|
|||
|
switch (visibility)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PRIVATE:
|
|||
|
visc = '0';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PROTECTED:
|
|||
|
visc = '1';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_VISIBILITY_PUBLIC:
|
|||
|
visc = '2';
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (constp)
|
|||
|
{
|
|||
|
if (volatilep)
|
|||
|
qualc = 'D';
|
|||
|
else
|
|||
|
qualc = 'B';
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (volatilep)
|
|||
|
qualc = 'C';
|
|||
|
else
|
|||
|
qualc = 'A';
|
|||
|
}
|
|||
|
|
|||
|
if (staticp)
|
|||
|
typec = '?';
|
|||
|
else if (! contextp)
|
|||
|
typec = '.';
|
|||
|
else
|
|||
|
typec = '*';
|
|||
|
|
|||
|
info->type_stack->methods =
|
|||
|
(char *) xrealloc (info->type_stack->methods,
|
|||
|
(strlen (info->type_stack->methods)
|
|||
|
+ strlen (type)
|
|||
|
+ strlen (physname)
|
|||
|
+ (contextp ? strlen (context) : 0)
|
|||
|
+ 40));
|
|||
|
|
|||
|
sprintf (info->type_stack->methods + strlen (info->type_stack->methods),
|
|||
|
"%s:%s;%c%c%c", type, physname, visc, qualc, typec);
|
|||
|
free (type);
|
|||
|
|
|||
|
if (contextp)
|
|||
|
{
|
|||
|
sprintf (info->type_stack->methods + strlen (info->type_stack->methods),
|
|||
|
"%ld;%s;", (long) voffset, context);
|
|||
|
free (context);
|
|||
|
}
|
|||
|
|
|||
|
if (definition)
|
|||
|
info->type_stack->definition = true;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Add a variant to the current method. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_method_variant (p, physname, visibility, constp, volatilep,
|
|||
|
voffset, contextp)
|
|||
|
PTR p;
|
|||
|
const char *physname;
|
|||
|
enum debug_visibility visibility;
|
|||
|
boolean constp;
|
|||
|
boolean volatilep;
|
|||
|
bfd_vma voffset;
|
|||
|
boolean contextp;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
return stab_class_method_var (info, physname, visibility, false, constp,
|
|||
|
volatilep, voffset, contextp);
|
|||
|
}
|
|||
|
|
|||
|
/* Add a static variant to the current method. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_static_method_variant (p, physname, visibility, constp, volatilep)
|
|||
|
PTR p;
|
|||
|
const char *physname;
|
|||
|
enum debug_visibility visibility;
|
|||
|
boolean constp;
|
|||
|
boolean volatilep;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
return stab_class_method_var (info, physname, visibility, true, constp,
|
|||
|
volatilep, 0, false);
|
|||
|
}
|
|||
|
|
|||
|
/* Finish up a method. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_class_end_method (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
assert (info->type_stack != NULL && info->type_stack->methods != NULL);
|
|||
|
|
|||
|
/* We allocated enough room on info->type_stack->methods to add the
|
|||
|
trailing semicolon. */
|
|||
|
strcat (info->type_stack->methods, ";");
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Finish up a class. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_end_class_type (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
size_t len;
|
|||
|
unsigned int i;
|
|||
|
char *buf;
|
|||
|
|
|||
|
assert (info->type_stack != NULL && info->type_stack->fields != NULL);
|
|||
|
|
|||
|
/* Work out the size we need to allocate for the class definition. */
|
|||
|
|
|||
|
len = (strlen (info->type_stack->string)
|
|||
|
+ strlen (info->type_stack->fields)
|
|||
|
+ 10);
|
|||
|
if (info->type_stack->baseclasses != NULL)
|
|||
|
{
|
|||
|
len += 20;
|
|||
|
for (i = 0; info->type_stack->baseclasses[i] != NULL; i++)
|
|||
|
len += strlen (info->type_stack->baseclasses[i]);
|
|||
|
}
|
|||
|
if (info->type_stack->methods != NULL)
|
|||
|
len += strlen (info->type_stack->methods);
|
|||
|
if (info->type_stack->vtable != NULL)
|
|||
|
len += strlen (info->type_stack->vtable);
|
|||
|
|
|||
|
/* Build the class definition. */
|
|||
|
|
|||
|
buf = (char *) xmalloc (len);
|
|||
|
|
|||
|
strcpy (buf, info->type_stack->string);
|
|||
|
|
|||
|
if (info->type_stack->baseclasses != NULL)
|
|||
|
{
|
|||
|
sprintf (buf + strlen (buf), "!%u,", i);
|
|||
|
for (i = 0; info->type_stack->baseclasses[i] != NULL; i++)
|
|||
|
{
|
|||
|
strcat (buf, info->type_stack->baseclasses[i]);
|
|||
|
free (info->type_stack->baseclasses[i]);
|
|||
|
}
|
|||
|
free (info->type_stack->baseclasses);
|
|||
|
info->type_stack->baseclasses = NULL;
|
|||
|
}
|
|||
|
|
|||
|
strcat (buf, info->type_stack->fields);
|
|||
|
free (info->type_stack->fields);
|
|||
|
info->type_stack->fields = NULL;
|
|||
|
|
|||
|
if (info->type_stack->methods != NULL)
|
|||
|
{
|
|||
|
strcat (buf, info->type_stack->methods);
|
|||
|
free (info->type_stack->methods);
|
|||
|
info->type_stack->methods = NULL;
|
|||
|
}
|
|||
|
|
|||
|
strcat (buf, ";");
|
|||
|
|
|||
|
if (info->type_stack->vtable != NULL)
|
|||
|
{
|
|||
|
strcat (buf, info->type_stack->vtable);
|
|||
|
free (info->type_stack->vtable);
|
|||
|
info->type_stack->vtable = NULL;
|
|||
|
}
|
|||
|
|
|||
|
/* Replace the string on the top of the stack with the complete
|
|||
|
class definition. */
|
|||
|
free (info->type_stack->string);
|
|||
|
info->type_stack->string = buf;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Push a typedef which was previously defined. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_typedef_type (p, name)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
struct string_hash_entry *h;
|
|||
|
|
|||
|
h = string_hash_lookup (&info->typedef_hash, name, false, false);
|
|||
|
assert (h != NULL && h->index > 0);
|
|||
|
|
|||
|
return stab_push_defined_type (info, h->index, h->size);
|
|||
|
}
|
|||
|
|
|||
|
/* Push a struct, union or class tag. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_tag_type (p, name, id, kind)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
unsigned int id;
|
|||
|
enum debug_type_kind kind;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
long index;
|
|||
|
unsigned int size;
|
|||
|
|
|||
|
index = stab_get_struct_index (info, name, id, kind, &size);
|
|||
|
if (index < 0)
|
|||
|
return false;
|
|||
|
|
|||
|
return stab_push_defined_type (info, index, size);
|
|||
|
}
|
|||
|
|
|||
|
/* Define a typedef. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_typdef (p, name)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
long index;
|
|||
|
unsigned int size;
|
|||
|
char *s, *buf;
|
|||
|
struct string_hash_entry *h;
|
|||
|
|
|||
|
index = info->type_stack->index;
|
|||
|
size = info->type_stack->size;
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + strlen (s) + 20);
|
|||
|
|
|||
|
if (index > 0)
|
|||
|
sprintf (buf, "%s:t%s", name, s);
|
|||
|
else
|
|||
|
{
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
sprintf (buf, "%s:t%ld=%s", name, index, s);
|
|||
|
}
|
|||
|
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
h = string_hash_lookup (&info->typedef_hash, name, true, false);
|
|||
|
if (h == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "string_hash_lookup failed: %s\n",
|
|||
|
bfd_errmsg (bfd_get_error ()));
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* I don't think we care about redefinitions. */
|
|||
|
|
|||
|
h->index = index;
|
|||
|
h->size = size;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Define a tag. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_tag (p, tag)
|
|||
|
PTR p;
|
|||
|
const char *tag;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *s, *buf;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (tag) + strlen (s) + 3);
|
|||
|
|
|||
|
sprintf (buf, "%s:T%s", tag, s);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Define an integer constant. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_int_constant (p, name, val)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
bfd_vma val;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *buf;
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + 20);
|
|||
|
sprintf (buf, "%s:c=i%ld", name, (long) val);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Define a floating point constant. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_float_constant (p, name, val)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
double val;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *buf;
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + 20);
|
|||
|
sprintf (buf, "%s:c=f%g", name, val);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Define a typed constant. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_typed_constant (p, name, val)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
bfd_vma val;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *s, *buf;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + strlen (s) + 20);
|
|||
|
sprintf (buf, "%s:c=e%s,%ld", name, s, (long) val);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_LSYM, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Record a variable. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_variable (p, name, kind, val)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
enum debug_var_kind kind;
|
|||
|
bfd_vma val;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *s, *buf;
|
|||
|
int stab_type;
|
|||
|
const char *kindstr;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
switch (kind)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
|
|||
|
case DEBUG_GLOBAL:
|
|||
|
stab_type = N_GSYM;
|
|||
|
kindstr = "G";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_STATIC:
|
|||
|
stab_type = N_STSYM;
|
|||
|
kindstr = "S";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_LOCAL_STATIC:
|
|||
|
stab_type = N_STSYM;
|
|||
|
kindstr = "V";
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_LOCAL:
|
|||
|
stab_type = N_LSYM;
|
|||
|
kindstr = "";
|
|||
|
|
|||
|
/* Make sure that this is a type reference or definition. */
|
|||
|
if (! isdigit (*s))
|
|||
|
{
|
|||
|
char *n;
|
|||
|
long index;
|
|||
|
|
|||
|
index = info->type_index;
|
|||
|
++info->type_index;
|
|||
|
n = (char *) xmalloc (strlen (s) + 20);
|
|||
|
sprintf (n, "%ld=%s", index, s);
|
|||
|
free (s);
|
|||
|
s = n;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_REGISTER:
|
|||
|
stab_type = N_RSYM;
|
|||
|
kindstr = "r";
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + strlen (s) + 3);
|
|||
|
sprintf (buf, "%s:%s%s", name, kindstr, s);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, stab_type, 0, val, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Start outputting a function. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_start_function (p, name, globalp)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
boolean globalp;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *rettype, *buf;
|
|||
|
|
|||
|
assert (info->nesting == 0 && info->fun_offset == -1);
|
|||
|
|
|||
|
rettype = stab_pop_type (info);
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + strlen (rettype) + 3);
|
|||
|
sprintf (buf, "%s:%c%s", name,
|
|||
|
globalp ? 'F' : 'f',
|
|||
|
rettype);
|
|||
|
|
|||
|
/* We don't know the value now, so we set it in start_block. */
|
|||
|
info->fun_offset = info->symbols_size;
|
|||
|
|
|||
|
if (! stab_write_symbol (info, N_FUN, 0, 0, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Output a function parameter. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_function_parameter (p, name, kind, val)
|
|||
|
PTR p;
|
|||
|
const char *name;
|
|||
|
enum debug_parm_kind kind;
|
|||
|
bfd_vma val;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
char *s, *buf;
|
|||
|
int stab_type;
|
|||
|
char kindc;
|
|||
|
|
|||
|
s = stab_pop_type (info);
|
|||
|
|
|||
|
switch (kind)
|
|||
|
{
|
|||
|
default:
|
|||
|
abort ();
|
|||
|
|
|||
|
case DEBUG_PARM_STACK:
|
|||
|
stab_type = N_PSYM;
|
|||
|
kindc = 'p';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_PARM_REG:
|
|||
|
stab_type = N_RSYM;
|
|||
|
kindc = 'P';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_PARM_REFERENCE:
|
|||
|
stab_type = N_PSYM;
|
|||
|
kindc = 'v';
|
|||
|
break;
|
|||
|
|
|||
|
case DEBUG_PARM_REF_REG:
|
|||
|
stab_type = N_RSYM;
|
|||
|
kindc = 'a';
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
buf = (char *) xmalloc (strlen (name) + strlen (s) + 3);
|
|||
|
sprintf (buf, "%s:%c%s", name, kindc, s);
|
|||
|
free (s);
|
|||
|
|
|||
|
if (! stab_write_symbol (info, stab_type, 0, val, buf))
|
|||
|
return false;
|
|||
|
|
|||
|
free (buf);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Start a block. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_start_block (p, addr)
|
|||
|
PTR p;
|
|||
|
bfd_vma addr;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
/* Fill in any slots which have been waiting for the first known
|
|||
|
text address. */
|
|||
|
|
|||
|
if (info->so_offset != -1)
|
|||
|
{
|
|||
|
bfd_put_32 (info->abfd, addr, info->symbols + info->so_offset + 8);
|
|||
|
info->so_offset = -1;
|
|||
|
}
|
|||
|
|
|||
|
if (info->fun_offset != -1)
|
|||
|
{
|
|||
|
bfd_put_32 (info->abfd, addr, info->symbols + info->fun_offset + 8);
|
|||
|
info->fun_offset = -1;
|
|||
|
}
|
|||
|
|
|||
|
++info->nesting;
|
|||
|
|
|||
|
/* We will be called with a top level block surrounding the
|
|||
|
function, but stabs information does not output that block, so we
|
|||
|
ignore it. */
|
|||
|
|
|||
|
if (info->nesting == 1)
|
|||
|
{
|
|||
|
info->fnaddr = addr;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* We have to output the LBRAC symbol after any variables which are
|
|||
|
declared inside the block. We postpone the LBRAC until the next
|
|||
|
start_block or end_block. */
|
|||
|
|
|||
|
/* If we have postponed an LBRAC, output it now. */
|
|||
|
if (info->pending_lbrac != (bfd_vma) -1)
|
|||
|
{
|
|||
|
if (! stab_write_symbol (info, N_LBRAC, 0, info->pending_lbrac,
|
|||
|
(const char *) NULL))
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Remember the address and output it later. */
|
|||
|
|
|||
|
info->pending_lbrac = addr - info->fnaddr;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* End a block. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_end_block (p, addr)
|
|||
|
PTR p;
|
|||
|
bfd_vma addr;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
if (addr > info->last_text_address)
|
|||
|
info->last_text_address = addr;
|
|||
|
|
|||
|
/* If we have postponed an LBRAC, output it now. */
|
|||
|
if (info->pending_lbrac != (bfd_vma) -1)
|
|||
|
{
|
|||
|
if (! stab_write_symbol (info, N_LBRAC, 0, info->pending_lbrac,
|
|||
|
(const char *) NULL))
|
|||
|
return false;
|
|||
|
info->pending_lbrac = (bfd_vma) -1;
|
|||
|
}
|
|||
|
|
|||
|
assert (info->nesting > 0);
|
|||
|
|
|||
|
--info->nesting;
|
|||
|
|
|||
|
/* We ignore the outermost block. */
|
|||
|
if (info->nesting == 0)
|
|||
|
return true;
|
|||
|
|
|||
|
return stab_write_symbol (info, N_RBRAC, 0, addr - info->fnaddr,
|
|||
|
(const char *) NULL);
|
|||
|
}
|
|||
|
|
|||
|
/* End a function. */
|
|||
|
|
|||
|
/*ARGSUSED*/
|
|||
|
static boolean
|
|||
|
stab_end_function (p)
|
|||
|
PTR p;
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/* Output a line number. */
|
|||
|
|
|||
|
static boolean
|
|||
|
stab_lineno (p, file, lineno, addr)
|
|||
|
PTR p;
|
|||
|
const char *file;
|
|||
|
unsigned long lineno;
|
|||
|
bfd_vma addr;
|
|||
|
{
|
|||
|
struct stab_write_handle *info = (struct stab_write_handle *) p;
|
|||
|
|
|||
|
assert (info->lineno_filename != NULL);
|
|||
|
|
|||
|
if (addr > info->last_text_address)
|
|||
|
info->last_text_address = addr;
|
|||
|
|
|||
|
if (strcmp (file, info->lineno_filename) != 0)
|
|||
|
{
|
|||
|
if (! stab_write_symbol (info, N_SOL, 0, addr, file))
|
|||
|
return false;
|
|||
|
info->lineno_filename = file;
|
|||
|
}
|
|||
|
|
|||
|
return stab_write_symbol (info, N_SLINE, lineno, addr - info->fnaddr,
|
|||
|
(const char *) NULL);
|
|||
|
}
|