2da42df615
unneeded (void *) casts. * ar.c: Likewise. * arlex.l: Likewise. * arparse.y: Likewise. * arsup.c: Likewise. * binemul.c: Likewise. * binemul.h: Likewise. * bucomm.c: Likewise. * bucomm.h: Likewise. * budbg.h: Likewise. * budemang.c: Likewise. * budemang.h: Likewise. * coffdump.c: Likewise. * coffgrok.c: Likewise. * cxxfilt.c: Likewise. * debug.c: Likewise. * debug.h: Likewise. * deflex.l: Likewise. * dlltool.c: Likewise. * dlltool.h: Likewise. * dllwrap.c: Likewise. * emul_aix.c: Likewise. * filemode.c: Likewise. * ieee.c: Likewise. * nlmconv.c: Likewise. * nlmconv.h: Likewise. * nlmheader.y: Likewise. * nm.c: Likewise. * prdbg.c: Likewise. * rclex.l: Likewise. * rcparse.y: Likewise. * rdcoff.c: Likewise. * rddbg.c: Likewise. * rename.c: Likewise. * resbin.c: Likewise. * rescoff.c: Likewise. * resrc.c: Likewise. * size.c: Likewise. * srconv.c: Likewise. * stabs.c: Likewise. * strings.c: Likewise. * sysdump.c: Likewise. * sysinfo.y: Likewise. * syslex.l: Likewise. * unwind-ia64.c: Likewise. * unwind-ia64.h: Likewise. * version.c: Likewise. * windres.c: Likewise. * windres.h: Likewise. * winduni.c: Likewise. * wrstabs.c: Likewise.
5048 lines
121 KiB
C
5048 lines
121 KiB
C
/* stabs.c -- Parse stabs debugging information
|
||
Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
|
||
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 parses stabs debugging information.
|
||
The organization of this code is based on the gdb stabs reading
|
||
code. The job it does is somewhat different, because it is not
|
||
trying to identify the correct address for anything. */
|
||
|
||
#include <stdio.h>
|
||
|
||
#include "bfd.h"
|
||
#include "bucomm.h"
|
||
#include "libiberty.h"
|
||
#include "safe-ctype.h"
|
||
#include "demangle.h"
|
||
#include "debug.h"
|
||
#include "budbg.h"
|
||
#include "filenames.h"
|
||
#include "aout/aout64.h"
|
||
#include "aout/stab_gnu.h"
|
||
|
||
/* The number of predefined XCOFF types. */
|
||
|
||
#define XCOFF_TYPE_COUNT 34
|
||
|
||
/* This structure is used as a handle so that the stab parsing doesn't
|
||
need to use any static variables. */
|
||
|
||
struct stab_handle
|
||
{
|
||
/* The BFD. */
|
||
bfd *abfd;
|
||
/* TRUE if this is stabs in sections. */
|
||
bfd_boolean sections;
|
||
/* The symbol table. */
|
||
asymbol **syms;
|
||
/* The number of symbols. */
|
||
long symcount;
|
||
/* The accumulated file name string. */
|
||
char *so_string;
|
||
/* The value of the last N_SO symbol. */
|
||
bfd_vma so_value;
|
||
/* The value of the start of the file, so that we can handle file
|
||
relative N_LBRAC and N_RBRAC symbols. */
|
||
bfd_vma file_start_offset;
|
||
/* The offset of the start of the function, so that we can handle
|
||
function relative N_LBRAC and N_RBRAC symbols. */
|
||
bfd_vma function_start_offset;
|
||
/* The version number of gcc which compiled the current compilation
|
||
unit, 0 if not compiled by gcc. */
|
||
int gcc_compiled;
|
||
/* Whether an N_OPT symbol was seen that was not generated by gcc,
|
||
so that we can detect the SunPRO compiler. */
|
||
bfd_boolean n_opt_found;
|
||
/* The main file name. */
|
||
char *main_filename;
|
||
/* A stack of unfinished N_BINCL files. */
|
||
struct bincl_file *bincl_stack;
|
||
/* A list of finished N_BINCL files. */
|
||
struct bincl_file *bincl_list;
|
||
/* Whether we are inside a function or not. */
|
||
bfd_boolean within_function;
|
||
/* The address of the end of the function, used if we have seen an
|
||
N_FUN symbol while in a function. This is -1 if we have not seen
|
||
an N_FUN (the normal case). */
|
||
bfd_vma function_end;
|
||
/* The depth of block nesting. */
|
||
int block_depth;
|
||
/* List of pending variable definitions. */
|
||
struct stab_pending_var *pending;
|
||
/* Number of files for which we have types. */
|
||
unsigned int files;
|
||
/* Lists of types per file. */
|
||
struct stab_types **file_types;
|
||
/* Predefined XCOFF types. */
|
||
debug_type xcoff_types[XCOFF_TYPE_COUNT];
|
||
/* Undefined tags. */
|
||
struct stab_tag *tags;
|
||
/* Set by parse_stab_type if it sees a structure defined as a cross
|
||
reference to itself. Reset by parse_stab_type otherwise. */
|
||
bfd_boolean self_crossref;
|
||
};
|
||
|
||
/* A list of these structures is used to hold pending variable
|
||
definitions seen before the N_LBRAC of a block. */
|
||
|
||
struct stab_pending_var
|
||
{
|
||
/* Next pending variable definition. */
|
||
struct stab_pending_var *next;
|
||
/* Name. */
|
||
const char *name;
|
||
/* Type. */
|
||
debug_type type;
|
||
/* Kind. */
|
||
enum debug_var_kind kind;
|
||
/* Value. */
|
||
bfd_vma val;
|
||
};
|
||
|
||
/* A list of these structures is used to hold the types for a single
|
||
file. */
|
||
|
||
struct stab_types
|
||
{
|
||
/* Next set of slots for this file. */
|
||
struct stab_types *next;
|
||
/* Types indexed by type number. */
|
||
#define STAB_TYPES_SLOTS (16)
|
||
debug_type types[STAB_TYPES_SLOTS];
|
||
};
|
||
|
||
/* We keep a list of undefined tags that we encounter, so that we can
|
||
fill them in if the tag is later defined. */
|
||
|
||
struct stab_tag
|
||
{
|
||
/* Next undefined tag. */
|
||
struct stab_tag *next;
|
||
/* Tag name. */
|
||
const char *name;
|
||
/* Type kind. */
|
||
enum debug_type_kind kind;
|
||
/* Slot to hold real type when we discover it. If we don't, we fill
|
||
in an undefined tag type. */
|
||
debug_type slot;
|
||
/* Indirect type we have created to point at slot. */
|
||
debug_type type;
|
||
};
|
||
|
||
static char *savestring (const char *, int);
|
||
static bfd_vma parse_number (const char **, bfd_boolean *);
|
||
static void bad_stab (const char *);
|
||
static void warn_stab (const char *, const char *);
|
||
static bfd_boolean parse_stab_string
|
||
(void *, struct stab_handle *, int, int, bfd_vma, const char *);
|
||
static debug_type parse_stab_type
|
||
(void *, struct stab_handle *, const char *, const char **, debug_type **);
|
||
static bfd_boolean parse_stab_type_number (const char **, int *);
|
||
static debug_type parse_stab_range_type
|
||
(void *, struct stab_handle *, const char *, const char **, const int *);
|
||
static debug_type parse_stab_sun_builtin_type (void *, const char **);
|
||
static debug_type parse_stab_sun_floating_type (void *, const char **);
|
||
static debug_type parse_stab_enum_type (void *, const char **);
|
||
static debug_type parse_stab_struct_type
|
||
(void *, struct stab_handle *, const char *, const char **,
|
||
bfd_boolean, const int *);
|
||
static bfd_boolean parse_stab_baseclasses
|
||
(void *, struct stab_handle *, const char **, debug_baseclass **);
|
||
static bfd_boolean parse_stab_struct_fields
|
||
(void *, struct stab_handle *, const char **, debug_field **, bfd_boolean *);
|
||
static bfd_boolean parse_stab_cpp_abbrev
|
||
(void *, struct stab_handle *, const char **, debug_field *);
|
||
static bfd_boolean parse_stab_one_struct_field
|
||
(void *, struct stab_handle *, const char **, const char *,
|
||
debug_field *, bfd_boolean *);
|
||
static bfd_boolean parse_stab_members
|
||
(void *, struct stab_handle *, const char *, const char **, const int *,
|
||
debug_method **);
|
||
static debug_type parse_stab_argtypes
|
||
(void *, struct stab_handle *, debug_type, const char *, const char *,
|
||
debug_type, const char *, bfd_boolean, bfd_boolean, const char **);
|
||
static bfd_boolean parse_stab_tilde_field
|
||
(void *, struct stab_handle *, const char **, const int *, debug_type *,
|
||
bfd_boolean *);
|
||
static debug_type parse_stab_array_type
|
||
(void *, struct stab_handle *, const char **, bfd_boolean);
|
||
static void push_bincl (struct stab_handle *, const char *, bfd_vma);
|
||
static const char *pop_bincl (struct stab_handle *);
|
||
static bfd_boolean find_excl (struct stab_handle *, const char *, bfd_vma);
|
||
static bfd_boolean stab_record_variable
|
||
(void *, struct stab_handle *, const char *, debug_type,
|
||
enum debug_var_kind, bfd_vma);
|
||
static bfd_boolean stab_emit_pending_vars (void *, struct stab_handle *);
|
||
static debug_type *stab_find_slot (struct stab_handle *, const int *);
|
||
static debug_type stab_find_type (void *, struct stab_handle *, const int *);
|
||
static bfd_boolean stab_record_type
|
||
(void *, struct stab_handle *, const int *, debug_type);
|
||
static debug_type stab_xcoff_builtin_type
|
||
(void *, struct stab_handle *, int);
|
||
static debug_type stab_find_tagged_type
|
||
(void *, struct stab_handle *, const char *, int, enum debug_type_kind);
|
||
static debug_type *stab_demangle_argtypes
|
||
(void *, struct stab_handle *, const char *, bfd_boolean *, unsigned int);
|
||
|
||
/* Save a string in memory. */
|
||
|
||
static char *
|
||
savestring (const char *start, int len)
|
||
{
|
||
char *ret;
|
||
|
||
ret = (char *) xmalloc (len + 1);
|
||
memcpy (ret, start, len);
|
||
ret[len] = '\0';
|
||
return ret;
|
||
}
|
||
|
||
/* Read a number from a string. */
|
||
|
||
static bfd_vma
|
||
parse_number (const char **pp, bfd_boolean *poverflow)
|
||
{
|
||
unsigned long ul;
|
||
const char *orig;
|
||
|
||
if (poverflow != NULL)
|
||
*poverflow = FALSE;
|
||
|
||
orig = *pp;
|
||
|
||
errno = 0;
|
||
ul = strtoul (*pp, (char **) pp, 0);
|
||
if (ul + 1 != 0 || errno == 0)
|
||
{
|
||
/* If bfd_vma is larger than unsigned long, and the number is
|
||
meant to be negative, we have to make sure that we sign
|
||
extend properly. */
|
||
if (*orig == '-')
|
||
return (bfd_vma) (bfd_signed_vma) (long) ul;
|
||
return (bfd_vma) ul;
|
||
}
|
||
|
||
/* Note that even though strtoul overflowed, it should have set *pp
|
||
to the end of the number, which is where we want it. */
|
||
if (sizeof (bfd_vma) > sizeof (unsigned long))
|
||
{
|
||
const char *p;
|
||
bfd_boolean neg;
|
||
int base;
|
||
bfd_vma over, lastdig;
|
||
bfd_boolean overflow;
|
||
bfd_vma v;
|
||
|
||
/* Our own version of strtoul, for a bfd_vma. */
|
||
p = orig;
|
||
|
||
neg = FALSE;
|
||
if (*p == '+')
|
||
++p;
|
||
else if (*p == '-')
|
||
{
|
||
neg = TRUE;
|
||
++p;
|
||
}
|
||
|
||
base = 10;
|
||
if (*p == '0')
|
||
{
|
||
if (p[1] == 'x' || p[1] == 'X')
|
||
{
|
||
base = 16;
|
||
p += 2;
|
||
}
|
||
else
|
||
{
|
||
base = 8;
|
||
++p;
|
||
}
|
||
}
|
||
|
||
over = ((bfd_vma) (bfd_signed_vma) -1) / (bfd_vma) base;
|
||
lastdig = ((bfd_vma) (bfd_signed_vma) -1) % (bfd_vma) base;
|
||
|
||
overflow = FALSE;
|
||
v = 0;
|
||
while (1)
|
||
{
|
||
int d;
|
||
|
||
d = *p++;
|
||
if (ISDIGIT (d))
|
||
d -= '0';
|
||
else if (ISUPPER (d))
|
||
d -= 'A';
|
||
else if (ISLOWER (d))
|
||
d -= 'a';
|
||
else
|
||
break;
|
||
|
||
if (d >= base)
|
||
break;
|
||
|
||
if (v > over || (v == over && (bfd_vma) d > lastdig))
|
||
{
|
||
overflow = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (! overflow)
|
||
{
|
||
if (neg)
|
||
v = - v;
|
||
return v;
|
||
}
|
||
}
|
||
|
||
/* If we get here, the number is too large to represent in a
|
||
bfd_vma. */
|
||
if (poverflow != NULL)
|
||
*poverflow = TRUE;
|
||
else
|
||
warn_stab (orig, _("numeric overflow"));
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Give an error for a bad stab string. */
|
||
|
||
static void
|
||
bad_stab (const char *p)
|
||
{
|
||
fprintf (stderr, _("Bad stab: %s\n"), p);
|
||
}
|
||
|
||
/* Warn about something in a stab string. */
|
||
|
||
static void
|
||
warn_stab (const char *p, const char *err)
|
||
{
|
||
fprintf (stderr, _("Warning: %s: %s\n"), err, p);
|
||
}
|
||
|
||
/* Create a handle to parse stabs symbols with. */
|
||
|
||
void *
|
||
start_stab (void *dhandle ATTRIBUTE_UNUSED, bfd *abfd, bfd_boolean sections,
|
||
asymbol **syms, long symcount)
|
||
{
|
||
struct stab_handle *ret;
|
||
|
||
ret = (struct stab_handle *) xmalloc (sizeof *ret);
|
||
memset (ret, 0, sizeof *ret);
|
||
ret->abfd = abfd;
|
||
ret->sections = sections;
|
||
ret->syms = syms;
|
||
ret->symcount = symcount;
|
||
ret->files = 1;
|
||
ret->file_types = (struct stab_types **) xmalloc (sizeof *ret->file_types);
|
||
ret->file_types[0] = NULL;
|
||
ret->function_end = (bfd_vma) -1;
|
||
return (void *) ret;
|
||
}
|
||
|
||
/* When we have processed all the stabs information, we need to go
|
||
through and fill in all the undefined tags. */
|
||
|
||
bfd_boolean
|
||
finish_stab (void *dhandle, void *handle)
|
||
{
|
||
struct stab_handle *info = (struct stab_handle *) handle;
|
||
struct stab_tag *st;
|
||
|
||
if (info->within_function)
|
||
{
|
||
if (! stab_emit_pending_vars (dhandle, info)
|
||
|| ! debug_end_function (dhandle, info->function_end))
|
||
return FALSE;
|
||
info->within_function = FALSE;
|
||
info->function_end = (bfd_vma) -1;
|
||
}
|
||
|
||
for (st = info->tags; st != NULL; st = st->next)
|
||
{
|
||
enum debug_type_kind kind;
|
||
|
||
kind = st->kind;
|
||
if (kind == DEBUG_KIND_ILLEGAL)
|
||
kind = DEBUG_KIND_STRUCT;
|
||
st->slot = debug_make_undefined_tagged_type (dhandle, st->name, kind);
|
||
if (st->slot == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Handle a single stabs symbol. */
|
||
|
||
bfd_boolean
|
||
parse_stab (void *dhandle, void *handle, int type, int desc, bfd_vma value,
|
||
const char *string)
|
||
{
|
||
struct stab_handle *info = (struct stab_handle *) handle;
|
||
|
||
/* gcc will emit two N_SO strings per compilation unit, one for the
|
||
directory name and one for the file name. We just collect N_SO
|
||
strings as we see them, and start the new compilation unit when
|
||
we see a non N_SO symbol. */
|
||
if (info->so_string != NULL
|
||
&& (type != N_SO || *string == '\0' || value != info->so_value))
|
||
{
|
||
if (! debug_set_filename (dhandle, info->so_string))
|
||
return FALSE;
|
||
info->main_filename = info->so_string;
|
||
|
||
info->gcc_compiled = 0;
|
||
info->n_opt_found = FALSE;
|
||
|
||
/* Generally, for stabs in the symbol table, the N_LBRAC and
|
||
N_RBRAC symbols are relative to the N_SO symbol value. */
|
||
if (! info->sections)
|
||
info->file_start_offset = info->so_value;
|
||
|
||
/* We need to reset the mapping from type numbers to types. We
|
||
can't free the old mapping, because of the use of
|
||
debug_make_indirect_type. */
|
||
info->files = 1;
|
||
info->file_types = ((struct stab_types **)
|
||
xmalloc (sizeof *info->file_types));
|
||
info->file_types[0] = NULL;
|
||
|
||
info->so_string = NULL;
|
||
|
||
/* Now process whatever type we just got. */
|
||
}
|
||
|
||
switch (type)
|
||
{
|
||
case N_FN:
|
||
case N_FN_SEQ:
|
||
break;
|
||
|
||
case N_LBRAC:
|
||
/* Ignore extra outermost context from SunPRO cc and acc. */
|
||
if (info->n_opt_found && desc == 1)
|
||
break;
|
||
|
||
if (! info->within_function)
|
||
{
|
||
fprintf (stderr, _("N_LBRAC not within function\n"));
|
||
return FALSE;
|
||
}
|
||
|
||
/* Start an inner lexical block. */
|
||
if (! debug_start_block (dhandle,
|
||
(value
|
||
+ info->file_start_offset
|
||
+ info->function_start_offset)))
|
||
return FALSE;
|
||
|
||
/* Emit any pending variable definitions. */
|
||
if (! stab_emit_pending_vars (dhandle, info))
|
||
return FALSE;
|
||
|
||
++info->block_depth;
|
||
break;
|
||
|
||
case N_RBRAC:
|
||
/* Ignore extra outermost context from SunPRO cc and acc. */
|
||
if (info->n_opt_found && desc == 1)
|
||
break;
|
||
|
||
/* We shouldn't have any pending variable definitions here, but,
|
||
if we do, we probably need to emit them before closing the
|
||
block. */
|
||
if (! stab_emit_pending_vars (dhandle, info))
|
||
return FALSE;
|
||
|
||
/* End an inner lexical block. */
|
||
if (! debug_end_block (dhandle,
|
||
(value
|
||
+ info->file_start_offset
|
||
+ info->function_start_offset)))
|
||
return FALSE;
|
||
|
||
--info->block_depth;
|
||
if (info->block_depth < 0)
|
||
{
|
||
fprintf (stderr, _("Too many N_RBRACs\n"));
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
case N_SO:
|
||
/* This always ends a function. */
|
||
if (info->within_function)
|
||
{
|
||
bfd_vma endval;
|
||
|
||
endval = value;
|
||
if (*string != '\0'
|
||
&& info->function_end != (bfd_vma) -1
|
||
&& info->function_end < endval)
|
||
endval = info->function_end;
|
||
if (! stab_emit_pending_vars (dhandle, info)
|
||
|| ! debug_end_function (dhandle, endval))
|
||
return FALSE;
|
||
info->within_function = FALSE;
|
||
info->function_end = (bfd_vma) -1;
|
||
}
|
||
|
||
/* An empty string is emitted by gcc at the end of a compilation
|
||
unit. */
|
||
if (*string == '\0')
|
||
return TRUE;
|
||
|
||
/* Just accumulate strings until we see a non N_SO symbol. If
|
||
the string starts with a directory separator or some other
|
||
form of absolute path specification, we discard the previously
|
||
accumulated strings. */
|
||
if (info->so_string == NULL)
|
||
info->so_string = xstrdup (string);
|
||
else
|
||
{
|
||
char *f;
|
||
|
||
f = info->so_string;
|
||
|
||
if (IS_ABSOLUTE_PATH (string))
|
||
info->so_string = xstrdup (string);
|
||
else
|
||
info->so_string = concat (info->so_string, string,
|
||
(const char *) NULL);
|
||
free (f);
|
||
}
|
||
|
||
info->so_value = value;
|
||
|
||
break;
|
||
|
||
case N_SOL:
|
||
/* Start an include file. */
|
||
if (! debug_start_source (dhandle, string))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_BINCL:
|
||
/* Start an include file which may be replaced. */
|
||
push_bincl (info, string, value);
|
||
if (! debug_start_source (dhandle, string))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_EINCL:
|
||
/* End an N_BINCL include. */
|
||
if (! debug_start_source (dhandle, pop_bincl (info)))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_EXCL:
|
||
/* This is a duplicate of a header file named by N_BINCL which
|
||
was eliminated by the linker. */
|
||
if (! find_excl (info, string, value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_SLINE:
|
||
if (! debug_record_line (dhandle, desc,
|
||
value + (info->within_function
|
||
? info->function_start_offset : 0)))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_BCOMM:
|
||
if (! debug_start_common_block (dhandle, string))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_ECOMM:
|
||
if (! debug_end_common_block (dhandle, string))
|
||
return FALSE;
|
||
break;
|
||
|
||
case N_FUN:
|
||
if (*string == '\0')
|
||
{
|
||
if (info->within_function)
|
||
{
|
||
/* This always marks the end of a function; we don't
|
||
need to worry about info->function_end. */
|
||
if (info->sections)
|
||
value += info->function_start_offset;
|
||
if (! stab_emit_pending_vars (dhandle, info)
|
||
|| ! debug_end_function (dhandle, value))
|
||
return FALSE;
|
||
info->within_function = FALSE;
|
||
info->function_end = (bfd_vma) -1;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/* A const static symbol in the .text section will have an N_FUN
|
||
entry. We need to use these to mark the end of the function,
|
||
in case we are looking at gcc output before it was changed to
|
||
always emit an empty N_FUN. We can't call debug_end_function
|
||
here, because it might be a local static symbol. */
|
||
if (info->within_function
|
||
&& (info->function_end == (bfd_vma) -1
|
||
|| value < info->function_end))
|
||
info->function_end = value;
|
||
|
||
/* Fall through. */
|
||
/* FIXME: gdb checks the string for N_STSYM, N_LCSYM or N_ROSYM
|
||
symbols, and if it does not start with :S, gdb relocates the
|
||
value to the start of the section. gcc always seems to use
|
||
:S, so we don't worry about this. */
|
||
/* Fall through. */
|
||
default:
|
||
{
|
||
const char *colon;
|
||
|
||
colon = strchr (string, ':');
|
||
if (colon != NULL
|
||
&& (colon[1] == 'f' || colon[1] == 'F'))
|
||
{
|
||
if (info->within_function)
|
||
{
|
||
bfd_vma endval;
|
||
|
||
endval = value;
|
||
if (info->function_end != (bfd_vma) -1
|
||
&& info->function_end < endval)
|
||
endval = info->function_end;
|
||
if (! stab_emit_pending_vars (dhandle, info)
|
||
|| ! debug_end_function (dhandle, endval))
|
||
return FALSE;
|
||
info->function_end = (bfd_vma) -1;
|
||
}
|
||
/* For stabs in sections, line numbers and block addresses
|
||
are offsets from the start of the function. */
|
||
if (info->sections)
|
||
info->function_start_offset = value;
|
||
info->within_function = TRUE;
|
||
}
|
||
|
||
if (! parse_stab_string (dhandle, info, type, desc, value, string))
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
case N_OPT:
|
||
if (string != NULL && strcmp (string, "gcc2_compiled.") == 0)
|
||
info->gcc_compiled = 2;
|
||
else if (string != NULL && strcmp (string, "gcc_compiled.") == 0)
|
||
info->gcc_compiled = 1;
|
||
else
|
||
info->n_opt_found = TRUE;
|
||
break;
|
||
|
||
case N_OBJ:
|
||
case N_ENDM:
|
||
case N_MAIN:
|
||
case N_WARNING:
|
||
break;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Parse the stabs string. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_string (void *dhandle, struct stab_handle *info, int stabtype,
|
||
int desc, bfd_vma value, const char *string)
|
||
{
|
||
const char *p;
|
||
char *name;
|
||
int type;
|
||
debug_type dtype;
|
||
bfd_boolean synonym;
|
||
bfd_boolean self_crossref;
|
||
unsigned int lineno;
|
||
debug_type *slot;
|
||
|
||
p = strchr (string, ':');
|
||
if (p == NULL)
|
||
return TRUE;
|
||
|
||
while (p[1] == ':')
|
||
{
|
||
p += 2;
|
||
p = strchr (p, ':');
|
||
if (p == NULL)
|
||
{
|
||
bad_stab (string);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* GCC 2.x puts the line number in desc. SunOS apparently puts in
|
||
the number of bytes occupied by a type or object, which we
|
||
ignore. */
|
||
if (info->gcc_compiled >= 2)
|
||
lineno = desc;
|
||
else
|
||
lineno = 0;
|
||
|
||
/* FIXME: Sometimes the special C++ names start with '.'. */
|
||
name = NULL;
|
||
if (string[0] == '$')
|
||
{
|
||
switch (string[1])
|
||
{
|
||
case 't':
|
||
name = "this";
|
||
break;
|
||
case 'v':
|
||
/* Was: name = "vptr"; */
|
||
break;
|
||
case 'e':
|
||
name = "eh_throw";
|
||
break;
|
||
case '_':
|
||
/* This was an anonymous type that was never fixed up. */
|
||
break;
|
||
case 'X':
|
||
/* SunPRO (3.0 at least) static variable encoding. */
|
||
break;
|
||
default:
|
||
warn_stab (string, _("unknown C++ encoded name"));
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (name == NULL)
|
||
{
|
||
if (p == string || (string[0] == ' ' && p == string + 1))
|
||
name = NULL;
|
||
else
|
||
name = savestring (string, p - string);
|
||
}
|
||
|
||
++p;
|
||
if (ISDIGIT (*p) || *p == '(' || *p == '-')
|
||
type = 'l';
|
||
else
|
||
type = *p++;
|
||
|
||
switch (type)
|
||
{
|
||
case 'c':
|
||
/* c is a special case, not followed by a type-number.
|
||
SYMBOL:c=iVALUE for an integer constant symbol.
|
||
SYMBOL:c=rVALUE for a floating constant symbol.
|
||
SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol.
|
||
e.g. "b:c=e6,0" for "const b = blob1"
|
||
(where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */
|
||
if (*p != '=')
|
||
{
|
||
bad_stab (string);
|
||
return FALSE;
|
||
}
|
||
++p;
|
||
switch (*p++)
|
||
{
|
||
case 'r':
|
||
/* Floating point constant. */
|
||
if (! debug_record_float_const (dhandle, name, atof (p)))
|
||
return FALSE;
|
||
break;
|
||
case 'i':
|
||
/* Integer constant. */
|
||
/* Defining integer constants this way is kind of silly,
|
||
since 'e' constants allows the compiler to give not only
|
||
the value, but the type as well. C has at least int,
|
||
long, unsigned int, and long long as constant types;
|
||
other languages probably should have at least unsigned as
|
||
well as signed constants. */
|
||
if (! debug_record_int_const (dhandle, name, atoi (p)))
|
||
return FALSE;
|
||
break;
|
||
case 'e':
|
||
/* SYMBOL:c=eTYPE,INTVALUE for a constant symbol whose value
|
||
can be represented as integral.
|
||
e.g. "b:c=e6,0" for "const b = blob1"
|
||
(where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
&p, (debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (*p != ',')
|
||
{
|
||
bad_stab (string);
|
||
return FALSE;
|
||
}
|
||
if (! debug_record_typed_const (dhandle, name, dtype, atoi (p)))
|
||
return FALSE;
|
||
break;
|
||
default:
|
||
bad_stab (string);
|
||
return FALSE;
|
||
}
|
||
|
||
break;
|
||
|
||
case 'C':
|
||
/* The name of a caught exception. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
&p, (debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! debug_record_label (dhandle, name, dtype, value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'f':
|
||
case 'F':
|
||
/* A function definition. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! debug_record_function (dhandle, name, dtype, type == 'F', value))
|
||
return FALSE;
|
||
|
||
/* Sun acc puts declared types of arguments here. We don't care
|
||
about their actual types (FIXME -- we should remember the whole
|
||
function prototype), but the list may define some new types
|
||
that we have to remember, so we must scan it now. */
|
||
while (*p == ';')
|
||
{
|
||
++p;
|
||
if (parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL)
|
||
== DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
|
||
break;
|
||
|
||
case 'G':
|
||
{
|
||
char leading;
|
||
long c;
|
||
asymbol **ps;
|
||
|
||
/* A global symbol. The value must be extracted from the
|
||
symbol table. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
leading = bfd_get_symbol_leading_char (info->abfd);
|
||
for (c = info->symcount, ps = info->syms; c > 0; --c, ++ps)
|
||
{
|
||
const char *n;
|
||
|
||
n = bfd_asymbol_name (*ps);
|
||
if (leading != '\0' && *n == leading)
|
||
++n;
|
||
if (*n == *name && strcmp (n, name) == 0)
|
||
break;
|
||
}
|
||
if (c > 0)
|
||
value = bfd_asymbol_value (*ps);
|
||
if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_GLOBAL,
|
||
value))
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
/* This case is faked by a conditional above, when there is no
|
||
code letter in the dbx data. Dbx data never actually
|
||
contains 'l'. */
|
||
case 'l':
|
||
case 's':
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_LOCAL,
|
||
value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'p':
|
||
/* A function parameter. */
|
||
if (*p != 'F')
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
else
|
||
{
|
||
/* pF is a two-letter code that means a function parameter in
|
||
Fortran. The type-number specifies the type of the return
|
||
value. Translate it into a pointer-to-function type. */
|
||
++p;
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype != DEBUG_TYPE_NULL)
|
||
{
|
||
debug_type ftype;
|
||
|
||
ftype = debug_make_function_type (dhandle, dtype,
|
||
(debug_type *) NULL, FALSE);
|
||
dtype = debug_make_pointer_type (dhandle, ftype);
|
||
}
|
||
}
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_STACK,
|
||
value))
|
||
return FALSE;
|
||
|
||
/* FIXME: At this point gdb considers rearranging the parameter
|
||
address on a big endian machine if it is smaller than an int.
|
||
We have no way to do that, since we don't really know much
|
||
about the target. */
|
||
break;
|
||
|
||
case 'P':
|
||
if (stabtype == N_FUN)
|
||
{
|
||
/* Prototype of a function referenced by this file. */
|
||
while (*p == ';')
|
||
{
|
||
++p;
|
||
if (parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL)
|
||
== DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
break;
|
||
}
|
||
/* Fall through. */
|
||
case 'R':
|
||
/* Parameter which is in a register. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_REG,
|
||
value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'r':
|
||
/* Register variable (either global or local). */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_REGISTER,
|
||
value))
|
||
return FALSE;
|
||
|
||
/* FIXME: At this point gdb checks to combine pairs of 'p' and
|
||
'r' stabs into a single 'P' stab. */
|
||
break;
|
||
|
||
case 'S':
|
||
/* Static symbol at top level of file. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_STATIC,
|
||
value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 't':
|
||
/* A typedef. */
|
||
dtype = parse_stab_type (dhandle, info, name, &p, &slot);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (name == NULL)
|
||
{
|
||
/* A nameless type. Nothing to do. */
|
||
return TRUE;
|
||
}
|
||
|
||
dtype = debug_name_type (dhandle, name, dtype);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
|
||
if (slot != NULL)
|
||
*slot = dtype;
|
||
|
||
break;
|
||
|
||
case 'T':
|
||
/* Struct, union, or enum tag. For GNU C++, this can be be followed
|
||
by 't' which means we are typedef'ing it as well. */
|
||
if (*p != 't')
|
||
{
|
||
synonym = FALSE;
|
||
/* FIXME: gdb sets synonym to TRUE if the current language
|
||
is C++. */
|
||
}
|
||
else
|
||
{
|
||
synonym = TRUE;
|
||
++p;
|
||
}
|
||
|
||
dtype = parse_stab_type (dhandle, info, name, &p, &slot);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (name == NULL)
|
||
return TRUE;
|
||
|
||
/* INFO->SELF_CROSSREF is set by parse_stab_type if this type is
|
||
a cross reference to itself. These are generated by some
|
||
versions of g++. */
|
||
self_crossref = info->self_crossref;
|
||
|
||
dtype = debug_tag_type (dhandle, name, dtype);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (slot != NULL)
|
||
*slot = dtype;
|
||
|
||
/* See if we have a cross reference to this tag which we can now
|
||
fill in. Avoid filling in a cross reference to ourselves,
|
||
because that would lead to circular debugging information. */
|
||
if (! self_crossref)
|
||
{
|
||
register struct stab_tag **pst;
|
||
|
||
for (pst = &info->tags; *pst != NULL; pst = &(*pst)->next)
|
||
{
|
||
if ((*pst)->name[0] == name[0]
|
||
&& strcmp ((*pst)->name, name) == 0)
|
||
{
|
||
(*pst)->slot = dtype;
|
||
*pst = (*pst)->next;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (synonym)
|
||
{
|
||
dtype = debug_name_type (dhandle, name, dtype);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
|
||
if (slot != NULL)
|
||
*slot = dtype;
|
||
}
|
||
|
||
break;
|
||
|
||
case 'V':
|
||
/* Static symbol of local scope */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
/* FIXME: gdb checks os9k_stabs here. */
|
||
if (! stab_record_variable (dhandle, info, name, dtype,
|
||
DEBUG_LOCAL_STATIC, value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'v':
|
||
/* Reference parameter. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_REFERENCE,
|
||
value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'a':
|
||
/* Reference parameter which is in a register. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! debug_record_parameter (dhandle, name, dtype, DEBUG_PARM_REF_REG,
|
||
value))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'X':
|
||
/* This is used by Sun FORTRAN for "function result value".
|
||
Sun claims ("dbx and dbxtool interfaces", 2nd ed)
|
||
that Pascal uses it too, but when I tried it Pascal used
|
||
"x:3" (local symbol) instead. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL, &p,
|
||
(debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_LOCAL,
|
||
value))
|
||
return FALSE;
|
||
break;
|
||
|
||
default:
|
||
bad_stab (string);
|
||
return FALSE;
|
||
}
|
||
|
||
/* FIXME: gdb converts structure values to structure pointers in a
|
||
couple of cases, depending upon the target. */
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Parse a stabs type. The typename argument is non-NULL if this is a
|
||
typedef or a tag definition. The pp argument points to the stab
|
||
string, and is updated. The slotp argument points to a place to
|
||
store the slot used if the type is being defined. */
|
||
|
||
static debug_type
|
||
parse_stab_type (void *dhandle, struct stab_handle *info, const char *typename, const char **pp, debug_type **slotp)
|
||
{
|
||
const char *orig;
|
||
int typenums[2];
|
||
int size;
|
||
bfd_boolean stringp;
|
||
int descriptor;
|
||
debug_type dtype;
|
||
|
||
if (slotp != NULL)
|
||
*slotp = NULL;
|
||
|
||
orig = *pp;
|
||
|
||
size = -1;
|
||
stringp = FALSE;
|
||
|
||
info->self_crossref = FALSE;
|
||
|
||
/* Read type number if present. The type number may be omitted.
|
||
for instance in a two-dimensional array declared with type
|
||
"ar1;1;10;ar1;1;10;4". */
|
||
if (! ISDIGIT (**pp) && **pp != '(' && **pp != '-')
|
||
{
|
||
/* 'typenums=' not present, type is anonymous. Read and return
|
||
the definition, but don't put it in the type vector. */
|
||
typenums[0] = typenums[1] = -1;
|
||
}
|
||
else
|
||
{
|
||
if (! parse_stab_type_number (pp, typenums))
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (**pp != '=')
|
||
/* Type is not being defined here. Either it already
|
||
exists, or this is a forward reference to it. */
|
||
return stab_find_type (dhandle, info, typenums);
|
||
|
||
/* Only set the slot if the type is being defined. This means
|
||
that the mapping from type numbers to types will only record
|
||
the name of the typedef which defines a type. If we don't do
|
||
this, then something like
|
||
typedef int foo;
|
||
int i;
|
||
will record that i is of type foo. Unfortunately, stabs
|
||
information is ambiguous about variable types. For this code,
|
||
typedef int foo;
|
||
int i;
|
||
foo j;
|
||
the stabs information records both i and j as having the same
|
||
type. This could be fixed by patching the compiler. */
|
||
if (slotp != NULL && typenums[0] >= 0 && typenums[1] >= 0)
|
||
*slotp = stab_find_slot (info, typenums);
|
||
|
||
/* Type is being defined here. */
|
||
/* Skip the '='. */
|
||
++*pp;
|
||
|
||
while (**pp == '@')
|
||
{
|
||
const char *p = *pp + 1;
|
||
const char *attr;
|
||
|
||
if (ISDIGIT (*p) || *p == '(' || *p == '-')
|
||
/* Member type. */
|
||
break;
|
||
|
||
/* Type attributes. */
|
||
attr = p;
|
||
|
||
for (; *p != ';'; ++p)
|
||
{
|
||
if (*p == '\0')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
}
|
||
*pp = p + 1;
|
||
|
||
switch (*attr)
|
||
{
|
||
case 's':
|
||
size = atoi (attr + 1);
|
||
size /= 8; /* Size is in bits. We store it in bytes. */
|
||
if (size <= 0)
|
||
size = -1;
|
||
break;
|
||
|
||
case 'S':
|
||
stringp = TRUE;
|
||
break;
|
||
|
||
default:
|
||
/* Ignore unrecognized type attributes, so future
|
||
compilers can invent new ones. */
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
descriptor = **pp;
|
||
++*pp;
|
||
|
||
switch (descriptor)
|
||
{
|
||
case 'x':
|
||
{
|
||
enum debug_type_kind code;
|
||
const char *q1, *q2, *p;
|
||
|
||
/* A cross reference to another type. */
|
||
switch (**pp)
|
||
{
|
||
case 's':
|
||
code = DEBUG_KIND_STRUCT;
|
||
break;
|
||
case 'u':
|
||
code = DEBUG_KIND_UNION;
|
||
break;
|
||
case 'e':
|
||
code = DEBUG_KIND_ENUM;
|
||
break;
|
||
default:
|
||
/* Complain and keep going, so compilers can invent new
|
||
cross-reference types. */
|
||
warn_stab (orig, _("unrecognized cross reference type"));
|
||
code = DEBUG_KIND_STRUCT;
|
||
break;
|
||
}
|
||
++*pp;
|
||
|
||
q1 = strchr (*pp, '<');
|
||
p = strchr (*pp, ':');
|
||
if (p == NULL)
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
if (q1 != NULL && p > q1 && p[1] == ':')
|
||
{
|
||
int nest = 0;
|
||
|
||
for (q2 = q1; *q2 != '\0'; ++q2)
|
||
{
|
||
if (*q2 == '<')
|
||
++nest;
|
||
else if (*q2 == '>')
|
||
--nest;
|
||
else if (*q2 == ':' && nest == 0)
|
||
break;
|
||
}
|
||
p = q2;
|
||
if (*p != ':')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
}
|
||
|
||
/* Some versions of g++ can emit stabs like
|
||
fleep:T20=xsfleep:
|
||
which define structures in terms of themselves. We need to
|
||
tell the caller to avoid building a circular structure. */
|
||
if (typename != NULL
|
||
&& strncmp (typename, *pp, p - *pp) == 0
|
||
&& typename[p - *pp] == '\0')
|
||
info->self_crossref = TRUE;
|
||
|
||
dtype = stab_find_tagged_type (dhandle, info, *pp, p - *pp, code);
|
||
|
||
*pp = p + 1;
|
||
}
|
||
break;
|
||
|
||
case '-':
|
||
case '0':
|
||
case '1':
|
||
case '2':
|
||
case '3':
|
||
case '4':
|
||
case '5':
|
||
case '6':
|
||
case '7':
|
||
case '8':
|
||
case '9':
|
||
case '(':
|
||
{
|
||
const char *hold;
|
||
int xtypenums[2];
|
||
|
||
/* This type is defined as another type. */
|
||
(*pp)--;
|
||
hold = *pp;
|
||
|
||
/* Peek ahead at the number to detect void. */
|
||
if (! parse_stab_type_number (pp, xtypenums))
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (typenums[0] == xtypenums[0] && typenums[1] == xtypenums[1])
|
||
{
|
||
/* This type is being defined as itself, which means that
|
||
it is void. */
|
||
dtype = debug_make_void_type (dhandle);
|
||
}
|
||
else
|
||
{
|
||
*pp = hold;
|
||
|
||
/* Go back to the number and have parse_stab_type get it.
|
||
This means that we can deal with something like
|
||
t(1,2)=(3,4)=... which the Lucid compiler uses. */
|
||
dtype = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
pp, (debug_type **) NULL);
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
if (typenums[0] != -1)
|
||
{
|
||
if (! stab_record_type (dhandle, info, typenums, dtype))
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case '*':
|
||
dtype = debug_make_pointer_type (dhandle,
|
||
parse_stab_type (dhandle, info,
|
||
(const char *) NULL,
|
||
pp,
|
||
(debug_type **) NULL));
|
||
break;
|
||
|
||
case '&':
|
||
/* Reference to another type. */
|
||
dtype = (debug_make_reference_type
|
||
(dhandle,
|
||
parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL)));
|
||
break;
|
||
|
||
case 'f':
|
||
/* Function returning another type. */
|
||
/* FIXME: gdb checks os9k_stabs here. */
|
||
dtype = (debug_make_function_type
|
||
(dhandle,
|
||
parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL),
|
||
(debug_type *) NULL, FALSE));
|
||
break;
|
||
|
||
case 'k':
|
||
/* Const qualifier on some type (Sun). */
|
||
/* FIXME: gdb accepts 'c' here if os9k_stabs. */
|
||
dtype = debug_make_const_type (dhandle,
|
||
parse_stab_type (dhandle, info,
|
||
(const char *) NULL,
|
||
pp,
|
||
(debug_type **) NULL));
|
||
break;
|
||
|
||
case 'B':
|
||
/* Volatile qual on some type (Sun). */
|
||
/* FIXME: gdb accepts 'i' here if os9k_stabs. */
|
||
dtype = (debug_make_volatile_type
|
||
(dhandle,
|
||
parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL)));
|
||
break;
|
||
|
||
case '@':
|
||
/* Offset (class & variable) type. This is used for a pointer
|
||
relative to an object. */
|
||
{
|
||
debug_type domain;
|
||
debug_type memtype;
|
||
|
||
/* Member type. */
|
||
|
||
domain = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (domain == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
memtype = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (memtype == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
dtype = debug_make_offset_type (dhandle, domain, memtype);
|
||
}
|
||
break;
|
||
|
||
case '#':
|
||
/* Method (class & fn) type. */
|
||
if (**pp == '#')
|
||
{
|
||
debug_type return_type;
|
||
|
||
++*pp;
|
||
return_type = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
pp, (debug_type **) NULL);
|
||
if (return_type == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
dtype = debug_make_method_type (dhandle, return_type,
|
||
DEBUG_TYPE_NULL,
|
||
(debug_type *) NULL, FALSE);
|
||
}
|
||
else
|
||
{
|
||
debug_type domain;
|
||
debug_type return_type;
|
||
debug_type *args;
|
||
unsigned int n;
|
||
unsigned int alloc;
|
||
bfd_boolean varargs;
|
||
|
||
domain = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
pp, (debug_type **) NULL);
|
||
if (domain == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
return_type = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
pp, (debug_type **) NULL);
|
||
if (return_type == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
alloc = 10;
|
||
args = (debug_type *) xmalloc (alloc * sizeof *args);
|
||
n = 0;
|
||
while (**pp != ';')
|
||
{
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
if (n + 1 >= alloc)
|
||
{
|
||
alloc += 10;
|
||
args = ((debug_type *)
|
||
xrealloc (args, alloc * sizeof *args));
|
||
}
|
||
|
||
args[n] = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
pp, (debug_type **) NULL);
|
||
if (args[n] == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
++n;
|
||
}
|
||
++*pp;
|
||
|
||
/* If the last type is not void, then this function takes a
|
||
variable number of arguments. Otherwise, we must strip
|
||
the void type. */
|
||
if (n == 0
|
||
|| debug_get_type_kind (dhandle, args[n - 1]) != DEBUG_KIND_VOID)
|
||
varargs = TRUE;
|
||
else
|
||
{
|
||
--n;
|
||
varargs = FALSE;
|
||
}
|
||
|
||
args[n] = DEBUG_TYPE_NULL;
|
||
|
||
dtype = debug_make_method_type (dhandle, return_type, domain, args,
|
||
varargs);
|
||
}
|
||
break;
|
||
|
||
case 'r':
|
||
/* Range type. */
|
||
dtype = parse_stab_range_type (dhandle, info, typename, pp, typenums);
|
||
break;
|
||
|
||
case 'b':
|
||
/* FIXME: gdb checks os9k_stabs here. */
|
||
/* Sun ACC builtin int type. */
|
||
dtype = parse_stab_sun_builtin_type (dhandle, pp);
|
||
break;
|
||
|
||
case 'R':
|
||
/* Sun ACC builtin float type. */
|
||
dtype = parse_stab_sun_floating_type (dhandle, pp);
|
||
break;
|
||
|
||
case 'e':
|
||
/* Enumeration type. */
|
||
dtype = parse_stab_enum_type (dhandle, pp);
|
||
break;
|
||
|
||
case 's':
|
||
case 'u':
|
||
/* Struct or union type. */
|
||
dtype = parse_stab_struct_type (dhandle, info, typename, pp,
|
||
descriptor == 's', typenums);
|
||
break;
|
||
|
||
case 'a':
|
||
/* Array type. */
|
||
if (**pp != 'r')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
dtype = parse_stab_array_type (dhandle, info, pp, stringp);
|
||
break;
|
||
|
||
case 'S':
|
||
dtype = debug_make_set_type (dhandle,
|
||
parse_stab_type (dhandle, info,
|
||
(const char *) NULL,
|
||
pp,
|
||
(debug_type **) NULL),
|
||
stringp);
|
||
break;
|
||
|
||
default:
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
if (dtype == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (typenums[0] != -1)
|
||
{
|
||
if (! stab_record_type (dhandle, info, typenums, dtype))
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
if (size != -1)
|
||
{
|
||
if (! debug_record_type_size (dhandle, dtype, (unsigned int) size))
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
return dtype;
|
||
}
|
||
|
||
/* Read a number by which a type is referred to in dbx data, or
|
||
perhaps read a pair (FILENUM, TYPENUM) in parentheses. Just a
|
||
single number N is equivalent to (0,N). Return the two numbers by
|
||
storing them in the vector TYPENUMS. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_type_number (const char **pp, int *typenums)
|
||
{
|
||
const char *orig;
|
||
|
||
orig = *pp;
|
||
|
||
if (**pp != '(')
|
||
{
|
||
typenums[0] = 0;
|
||
typenums[1] = (int) parse_number (pp, (bfd_boolean *) NULL);
|
||
}
|
||
else
|
||
{
|
||
++*pp;
|
||
typenums[0] = (int) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
typenums[1] = (int) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ')')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Parse a range type. */
|
||
|
||
static debug_type
|
||
parse_stab_range_type (void *dhandle, struct stab_handle *info, const char *typename, const char **pp, const int *typenums)
|
||
{
|
||
const char *orig;
|
||
int rangenums[2];
|
||
bfd_boolean self_subrange;
|
||
debug_type index_type;
|
||
const char *s2, *s3;
|
||
bfd_signed_vma n2, n3;
|
||
bfd_boolean ov2, ov3;
|
||
|
||
orig = *pp;
|
||
|
||
index_type = DEBUG_TYPE_NULL;
|
||
|
||
/* First comes a type we are a subrange of.
|
||
In C it is usually 0, 1 or the type being defined. */
|
||
if (! parse_stab_type_number (pp, rangenums))
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
self_subrange = (rangenums[0] == typenums[0]
|
||
&& rangenums[1] == typenums[1]);
|
||
|
||
if (**pp == '=')
|
||
{
|
||
*pp = orig;
|
||
index_type = parse_stab_type (dhandle, info, (const char *) NULL,
|
||
pp, (debug_type **) NULL);
|
||
if (index_type == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
if (**pp == ';')
|
||
++*pp;
|
||
|
||
/* The remaining two operands are usually lower and upper bounds of
|
||
the range. But in some special cases they mean something else. */
|
||
s2 = *pp;
|
||
n2 = parse_number (pp, &ov2);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
s3 = *pp;
|
||
n3 = parse_number (pp, &ov3);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
if (ov2 || ov3)
|
||
{
|
||
/* gcc will emit range stabs for long long types. Handle this
|
||
as a special case. FIXME: This needs to be more general. */
|
||
#define LLLOW "01000000000000000000000;"
|
||
#define LLHIGH "0777777777777777777777;"
|
||
#define ULLHIGH "01777777777777777777777;"
|
||
if (index_type == DEBUG_TYPE_NULL)
|
||
{
|
||
if (strncmp (s2, LLLOW, sizeof LLLOW - 1) == 0
|
||
&& strncmp (s3, LLHIGH, sizeof LLHIGH - 1) == 0)
|
||
return debug_make_int_type (dhandle, 8, FALSE);
|
||
if (! ov2
|
||
&& n2 == 0
|
||
&& strncmp (s3, ULLHIGH, sizeof ULLHIGH - 1) == 0)
|
||
return debug_make_int_type (dhandle, 8, TRUE);
|
||
}
|
||
|
||
warn_stab (orig, _("numeric overflow"));
|
||
}
|
||
|
||
if (index_type == DEBUG_TYPE_NULL)
|
||
{
|
||
/* A type defined as a subrange of itself, with both bounds 0,
|
||
is void. */
|
||
if (self_subrange && n2 == 0 && n3 == 0)
|
||
return debug_make_void_type (dhandle);
|
||
|
||
/* A type defined as a subrange of itself, with n2 positive and
|
||
n3 zero, is a complex type, and n2 is the number of bytes. */
|
||
if (self_subrange && n3 == 0 && n2 > 0)
|
||
return debug_make_complex_type (dhandle, n2);
|
||
|
||
/* If n3 is zero and n2 is positive, this is a floating point
|
||
type, and n2 is the number of bytes. */
|
||
if (n3 == 0 && n2 > 0)
|
||
return debug_make_float_type (dhandle, n2);
|
||
|
||
/* If the upper bound is -1, this is an unsigned int. */
|
||
if (n2 == 0 && n3 == -1)
|
||
{
|
||
/* When gcc is used with -gstabs, but not -gstabs+, it will emit
|
||
long long int:t6=r1;0;-1;
|
||
long long unsigned int:t7=r1;0;-1;
|
||
We hack here to handle this reasonably. */
|
||
if (typename != NULL)
|
||
{
|
||
if (strcmp (typename, "long long int") == 0)
|
||
return debug_make_int_type (dhandle, 8, FALSE);
|
||
else if (strcmp (typename, "long long unsigned int") == 0)
|
||
return debug_make_int_type (dhandle, 8, TRUE);
|
||
}
|
||
/* FIXME: The size here really depends upon the target. */
|
||
return debug_make_int_type (dhandle, 4, TRUE);
|
||
}
|
||
|
||
/* A range of 0 to 127 is char. */
|
||
if (self_subrange && n2 == 0 && n3 == 127)
|
||
return debug_make_int_type (dhandle, 1, FALSE);
|
||
|
||
/* FIXME: gdb checks for the language CHILL here. */
|
||
|
||
if (n2 == 0)
|
||
{
|
||
if (n3 < 0)
|
||
return debug_make_int_type (dhandle, - n3, TRUE);
|
||
else if (n3 == 0xff)
|
||
return debug_make_int_type (dhandle, 1, TRUE);
|
||
else if (n3 == 0xffff)
|
||
return debug_make_int_type (dhandle, 2, TRUE);
|
||
else if (n3 == (bfd_signed_vma) 0xffffffff)
|
||
return debug_make_int_type (dhandle, 4, TRUE);
|
||
#ifdef BFD64
|
||
else if (n3 == ((((bfd_signed_vma) 0xffffffff) << 32) | 0xffffffff))
|
||
return debug_make_int_type (dhandle, 8, TRUE);
|
||
#endif
|
||
}
|
||
else if (n3 == 0
|
||
&& n2 < 0
|
||
&& (self_subrange || n2 == -8))
|
||
return debug_make_int_type (dhandle, - n2, TRUE);
|
||
else if (n2 == - n3 - 1 || n2 == n3 + 1)
|
||
{
|
||
if (n3 == 0x7f)
|
||
return debug_make_int_type (dhandle, 1, FALSE);
|
||
else if (n3 == 0x7fff)
|
||
return debug_make_int_type (dhandle, 2, FALSE);
|
||
else if (n3 == 0x7fffffff)
|
||
return debug_make_int_type (dhandle, 4, FALSE);
|
||
#ifdef BFD64
|
||
else if (n3 == ((((bfd_vma) 0x7fffffff) << 32) | 0xffffffff))
|
||
return debug_make_int_type (dhandle, 8, FALSE);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/* At this point I don't have the faintest idea how to deal with a
|
||
self_subrange type; I'm going to assume that this is used as an
|
||
idiom, and that all of them are special cases. So . . . */
|
||
if (self_subrange)
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
index_type = stab_find_type (dhandle, info, rangenums);
|
||
if (index_type == DEBUG_TYPE_NULL)
|
||
{
|
||
/* Does this actually ever happen? Is that why we are worrying
|
||
about dealing with it rather than just calling error_type? */
|
||
warn_stab (orig, _("missing index type"));
|
||
index_type = debug_make_int_type (dhandle, 4, FALSE);
|
||
}
|
||
|
||
return debug_make_range_type (dhandle, index_type, n2, n3);
|
||
}
|
||
|
||
/* Sun's ACC uses a somewhat saner method for specifying the builtin
|
||
typedefs in every file (for int, long, etc):
|
||
|
||
type = b <signed> <width>; <offset>; <nbits>
|
||
signed = u or s. Possible c in addition to u or s (for char?).
|
||
offset = offset from high order bit to start bit of type.
|
||
width is # bytes in object of this type, nbits is # bits in type.
|
||
|
||
The width/offset stuff appears to be for small objects stored in
|
||
larger ones (e.g. `shorts' in `int' registers). We ignore it for now,
|
||
FIXME. */
|
||
|
||
static debug_type
|
||
parse_stab_sun_builtin_type (void *dhandle, const char **pp)
|
||
{
|
||
const char *orig;
|
||
bfd_boolean unsignedp;
|
||
bfd_vma bits;
|
||
|
||
orig = *pp;
|
||
|
||
switch (**pp)
|
||
{
|
||
case 's':
|
||
unsignedp = FALSE;
|
||
break;
|
||
case 'u':
|
||
unsignedp = TRUE;
|
||
break;
|
||
default:
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
/* For some odd reason, all forms of char put a c here. This is strange
|
||
because no other type has this honor. We can safely ignore this because
|
||
we actually determine 'char'acterness by the number of bits specified in
|
||
the descriptor. */
|
||
if (**pp == 'c')
|
||
++*pp;
|
||
|
||
/* The first number appears to be the number of bytes occupied
|
||
by this type, except that unsigned short is 4 instead of 2.
|
||
Since this information is redundant with the third number,
|
||
we will ignore it. */
|
||
(void) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
/* The second number is always 0, so ignore it too. */
|
||
(void) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
/* The third number is the number of bits for this type. */
|
||
bits = parse_number (pp, (bfd_boolean *) NULL);
|
||
|
||
/* The type *should* end with a semicolon. If it are embedded
|
||
in a larger type the semicolon may be the only way to know where
|
||
the type ends. If this type is at the end of the stabstring we
|
||
can deal with the omitted semicolon (but we don't have to like
|
||
it). Don't bother to complain(), Sun's compiler omits the semicolon
|
||
for "void". */
|
||
if (**pp == ';')
|
||
++*pp;
|
||
|
||
if (bits == 0)
|
||
return debug_make_void_type (dhandle);
|
||
|
||
return debug_make_int_type (dhandle, bits / 8, unsignedp);
|
||
}
|
||
|
||
/* Parse a builtin floating type generated by the Sun compiler. */
|
||
|
||
static debug_type
|
||
parse_stab_sun_floating_type (void *dhandle, const char **pp)
|
||
{
|
||
const char *orig;
|
||
bfd_vma details;
|
||
bfd_vma bytes;
|
||
|
||
orig = *pp;
|
||
|
||
/* The first number has more details about the type, for example
|
||
FN_COMPLEX. */
|
||
details = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
/* The second number is the number of bytes occupied by this type */
|
||
bytes = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
|
||
if (details == NF_COMPLEX
|
||
|| details == NF_COMPLEX16
|
||
|| details == NF_COMPLEX32)
|
||
return debug_make_complex_type (dhandle, bytes);
|
||
|
||
return debug_make_float_type (dhandle, bytes);
|
||
}
|
||
|
||
/* Handle an enum type. */
|
||
|
||
static debug_type
|
||
parse_stab_enum_type (void *dhandle, const char **pp)
|
||
{
|
||
const char *orig;
|
||
const char **names;
|
||
bfd_signed_vma *values;
|
||
unsigned int n;
|
||
unsigned int alloc;
|
||
|
||
orig = *pp;
|
||
|
||
/* FIXME: gdb checks os9k_stabs here. */
|
||
|
||
/* The aix4 compiler emits an extra field before the enum members;
|
||
my guess is it's a type of some sort. Just ignore it. */
|
||
if (**pp == '-')
|
||
{
|
||
while (**pp != ':')
|
||
++*pp;
|
||
++*pp;
|
||
}
|
||
|
||
/* Read the value-names and their values.
|
||
The input syntax is NAME:VALUE,NAME:VALUE, and so on.
|
||
A semicolon or comma instead of a NAME means the end. */
|
||
alloc = 10;
|
||
names = (const char **) xmalloc (alloc * sizeof *names);
|
||
values = (bfd_signed_vma *) xmalloc (alloc * sizeof *values);
|
||
n = 0;
|
||
while (**pp != '\0' && **pp != ';' && **pp != ',')
|
||
{
|
||
const char *p;
|
||
char *name;
|
||
bfd_signed_vma val;
|
||
|
||
p = *pp;
|
||
while (*p != ':')
|
||
++p;
|
||
|
||
name = savestring (*pp, p - *pp);
|
||
|
||
*pp = p + 1;
|
||
val = (bfd_signed_vma) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
if (n + 1 >= alloc)
|
||
{
|
||
alloc += 10;
|
||
names = ((const char **)
|
||
xrealloc (names, alloc * sizeof *names));
|
||
values = ((bfd_signed_vma *)
|
||
xrealloc (values, alloc * sizeof *values));
|
||
}
|
||
|
||
names[n] = name;
|
||
values[n] = val;
|
||
++n;
|
||
}
|
||
|
||
names[n] = NULL;
|
||
values[n] = 0;
|
||
|
||
if (**pp == ';')
|
||
++*pp;
|
||
|
||
return debug_make_enum_type (dhandle, names, values);
|
||
}
|
||
|
||
/* Read the description of a structure (or union type) and return an object
|
||
describing the type.
|
||
|
||
PP points to a character pointer that points to the next unconsumed token
|
||
in the the stabs string. For example, given stabs "A:T4=s4a:1,0,32;;",
|
||
*PP will point to "4a:1,0,32;;". */
|
||
|
||
static debug_type
|
||
parse_stab_struct_type (void *dhandle, struct stab_handle *info,
|
||
const char *tagname, const char **pp,
|
||
bfd_boolean structp, const int *typenums)
|
||
{
|
||
const char *orig;
|
||
bfd_vma size;
|
||
debug_baseclass *baseclasses;
|
||
debug_field *fields;
|
||
bfd_boolean statics;
|
||
debug_method *methods;
|
||
debug_type vptrbase;
|
||
bfd_boolean ownvptr;
|
||
|
||
orig = *pp;
|
||
|
||
/* Get the size. */
|
||
size = parse_number (pp, (bfd_boolean *) NULL);
|
||
|
||
/* Get the other information. */
|
||
if (! parse_stab_baseclasses (dhandle, info, pp, &baseclasses)
|
||
|| ! parse_stab_struct_fields (dhandle, info, pp, &fields, &statics)
|
||
|| ! parse_stab_members (dhandle, info, tagname, pp, typenums, &methods)
|
||
|| ! parse_stab_tilde_field (dhandle, info, pp, typenums, &vptrbase,
|
||
&ownvptr))
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (! statics
|
||
&& baseclasses == NULL
|
||
&& methods == NULL
|
||
&& vptrbase == DEBUG_TYPE_NULL
|
||
&& ! ownvptr)
|
||
return debug_make_struct_type (dhandle, structp, size, fields);
|
||
|
||
return debug_make_object_type (dhandle, structp, size, fields, baseclasses,
|
||
methods, vptrbase, ownvptr);
|
||
}
|
||
|
||
/* The stabs for C++ derived classes contain baseclass information which
|
||
is marked by a '!' character after the total size. This function is
|
||
called when we encounter the baseclass marker, and slurps up all the
|
||
baseclass information.
|
||
|
||
Immediately following the '!' marker is the number of base classes that
|
||
the class is derived from, followed by information for each base class.
|
||
For each base class, there are two visibility specifiers, a bit offset
|
||
to the base class information within the derived class, a reference to
|
||
the type for the base class, and a terminating semicolon.
|
||
|
||
A typical example, with two base classes, would be "!2,020,19;0264,21;".
|
||
^^ ^ ^ ^ ^ ^ ^
|
||
Baseclass information marker __________________|| | | | | | |
|
||
Number of baseclasses __________________________| | | | | | |
|
||
Visibility specifiers (2) ________________________| | | | | |
|
||
Offset in bits from start of class _________________| | | | |
|
||
Type number for base class ___________________________| | | |
|
||
Visibility specifiers (2) _______________________________| | |
|
||
Offset in bits from start of class ________________________| |
|
||
Type number of base class ____________________________________|
|
||
|
||
Return TRUE for success, FALSE for failure. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_baseclasses (void *dhandle, struct stab_handle *info,
|
||
const char **pp, debug_baseclass **retp)
|
||
{
|
||
const char *orig;
|
||
unsigned int c, i;
|
||
debug_baseclass *classes;
|
||
|
||
*retp = NULL;
|
||
|
||
orig = *pp;
|
||
|
||
if (**pp != '!')
|
||
{
|
||
/* No base classes. */
|
||
return TRUE;
|
||
}
|
||
++*pp;
|
||
|
||
c = (unsigned int) parse_number (pp, (bfd_boolean *) NULL);
|
||
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
classes = (debug_baseclass *) xmalloc ((c + 1) * sizeof (**retp));
|
||
|
||
for (i = 0; i < c; i++)
|
||
{
|
||
bfd_boolean virtual;
|
||
enum debug_visibility visibility;
|
||
bfd_vma bitpos;
|
||
debug_type type;
|
||
|
||
switch (**pp)
|
||
{
|
||
case '0':
|
||
virtual = FALSE;
|
||
break;
|
||
case '1':
|
||
virtual = TRUE;
|
||
break;
|
||
default:
|
||
warn_stab (orig, _("unknown virtual character for baseclass"));
|
||
virtual = FALSE;
|
||
break;
|
||
}
|
||
++*pp;
|
||
|
||
switch (**pp)
|
||
{
|
||
case '0':
|
||
visibility = DEBUG_VISIBILITY_PRIVATE;
|
||
break;
|
||
case '1':
|
||
visibility = DEBUG_VISIBILITY_PROTECTED;
|
||
break;
|
||
case '2':
|
||
visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
break;
|
||
default:
|
||
warn_stab (orig, _("unknown visibility character for baseclass"));
|
||
visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
break;
|
||
}
|
||
++*pp;
|
||
|
||
/* The remaining value is the bit offset of the portion of the
|
||
object corresponding to this baseclass. Always zero in the
|
||
absence of multiple inheritance. */
|
||
bitpos = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
type = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
|
||
classes[i] = debug_make_baseclass (dhandle, type, bitpos, virtual,
|
||
visibility);
|
||
if (classes[i] == DEBUG_BASECLASS_NULL)
|
||
return FALSE;
|
||
|
||
if (**pp != ';')
|
||
return FALSE;
|
||
++*pp;
|
||
}
|
||
|
||
classes[i] = DEBUG_BASECLASS_NULL;
|
||
|
||
*retp = classes;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Read struct or class data fields. They have the form:
|
||
|
||
NAME : [VISIBILITY] TYPENUM , BITPOS , BITSIZE ;
|
||
|
||
At the end, we see a semicolon instead of a field.
|
||
|
||
In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for
|
||
a static field.
|
||
|
||
The optional VISIBILITY is one of:
|
||
|
||
'/0' (VISIBILITY_PRIVATE)
|
||
'/1' (VISIBILITY_PROTECTED)
|
||
'/2' (VISIBILITY_PUBLIC)
|
||
'/9' (VISIBILITY_IGNORE)
|
||
|
||
or nothing, for C style fields with public visibility.
|
||
|
||
Returns 1 for success, 0 for failure. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_struct_fields (void *dhandle, struct stab_handle *info,
|
||
const char **pp, debug_field **retp,
|
||
bfd_boolean *staticsp)
|
||
{
|
||
const char *orig;
|
||
const char *p;
|
||
debug_field *fields;
|
||
unsigned int c;
|
||
unsigned int alloc;
|
||
|
||
*retp = NULL;
|
||
*staticsp = FALSE;
|
||
|
||
orig = *pp;
|
||
|
||
c = 0;
|
||
alloc = 10;
|
||
fields = (debug_field *) xmalloc (alloc * sizeof *fields);
|
||
while (**pp != ';')
|
||
{
|
||
/* FIXME: gdb checks os9k_stabs here. */
|
||
|
||
p = *pp;
|
||
|
||
/* Add 1 to c to leave room for NULL pointer at end. */
|
||
if (c + 1 >= alloc)
|
||
{
|
||
alloc += 10;
|
||
fields = ((debug_field *)
|
||
xrealloc (fields, alloc * sizeof *fields));
|
||
}
|
||
|
||
/* If it starts with CPLUS_MARKER it is a special abbreviation,
|
||
unless the CPLUS_MARKER is followed by an underscore, in
|
||
which case it is just the name of an anonymous type, which we
|
||
should handle like any other type name. We accept either '$'
|
||
or '.', because a field name can never contain one of these
|
||
characters except as a CPLUS_MARKER. */
|
||
|
||
if ((*p == '$' || *p == '.') && p[1] != '_')
|
||
{
|
||
++*pp;
|
||
if (! parse_stab_cpp_abbrev (dhandle, info, pp, fields + c))
|
||
return FALSE;
|
||
++c;
|
||
continue;
|
||
}
|
||
|
||
/* Look for the ':' that separates the field name from the field
|
||
values. Data members are delimited by a single ':', while member
|
||
functions are delimited by a pair of ':'s. When we hit the member
|
||
functions (if any), terminate scan loop and return. */
|
||
|
||
p = strchr (p, ':');
|
||
if (p == NULL)
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
if (p[1] == ':')
|
||
break;
|
||
|
||
if (! parse_stab_one_struct_field (dhandle, info, pp, p, fields + c,
|
||
staticsp))
|
||
return FALSE;
|
||
|
||
++c;
|
||
}
|
||
|
||
fields[c] = DEBUG_FIELD_NULL;
|
||
|
||
*retp = fields;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Special GNU C++ name. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_cpp_abbrev (void *dhandle, struct stab_handle *info,
|
||
const char **pp, debug_field *retp)
|
||
{
|
||
const char *orig;
|
||
int cpp_abbrev;
|
||
debug_type context;
|
||
const char *name;
|
||
const char *typename;
|
||
debug_type type;
|
||
bfd_vma bitpos;
|
||
|
||
*retp = DEBUG_FIELD_NULL;
|
||
|
||
orig = *pp;
|
||
|
||
if (**pp != 'v')
|
||
{
|
||
bad_stab (*pp);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
cpp_abbrev = **pp;
|
||
++*pp;
|
||
|
||
/* At this point, *pp points to something like "22:23=*22...", where
|
||
the type number before the ':' is the "context" and everything
|
||
after is a regular type definition. Lookup the type, find it's
|
||
name, and construct the field name. */
|
||
|
||
context = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (context == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
|
||
switch (cpp_abbrev)
|
||
{
|
||
case 'f':
|
||
/* $vf -- a virtual function table pointer. */
|
||
name = "_vptr$";
|
||
break;
|
||
case 'b':
|
||
/* $vb -- a virtual bsomethingorother */
|
||
typename = debug_get_type_name (dhandle, context);
|
||
if (typename == NULL)
|
||
{
|
||
warn_stab (orig, _("unnamed $vb type"));
|
||
typename = "FOO";
|
||
}
|
||
name = concat ("_vb$", typename, (const char *) NULL);
|
||
break;
|
||
default:
|
||
warn_stab (orig, _("unrecognized C++ abbreviation"));
|
||
name = "INVALID_CPLUSPLUS_ABBREV";
|
||
break;
|
||
}
|
||
|
||
if (**pp != ':')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
type = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
bitpos = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
*retp = debug_make_field (dhandle, name, type, bitpos, 0,
|
||
DEBUG_VISIBILITY_PRIVATE);
|
||
if (*retp == DEBUG_FIELD_NULL)
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Parse a single field in a struct or union. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_one_struct_field (void *dhandle, struct stab_handle *info,
|
||
const char **pp, const char *p,
|
||
debug_field *retp, bfd_boolean *staticsp)
|
||
{
|
||
const char *orig;
|
||
char *name;
|
||
enum debug_visibility visibility;
|
||
debug_type type;
|
||
bfd_vma bitpos;
|
||
bfd_vma bitsize;
|
||
|
||
orig = *pp;
|
||
|
||
/* FIXME: gdb checks ARM_DEMANGLING here. */
|
||
|
||
name = savestring (*pp, p - *pp);
|
||
|
||
*pp = p + 1;
|
||
|
||
if (**pp != '/')
|
||
visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
else
|
||
{
|
||
++*pp;
|
||
switch (**pp)
|
||
{
|
||
case '0':
|
||
visibility = DEBUG_VISIBILITY_PRIVATE;
|
||
break;
|
||
case '1':
|
||
visibility = DEBUG_VISIBILITY_PROTECTED;
|
||
break;
|
||
case '2':
|
||
visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
break;
|
||
default:
|
||
warn_stab (orig, _("unknown visibility character for field"));
|
||
visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
break;
|
||
}
|
||
++*pp;
|
||
}
|
||
|
||
type = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
|
||
if (**pp == ':')
|
||
{
|
||
char *varname;
|
||
|
||
/* This is a static class member. */
|
||
++*pp;
|
||
p = strchr (*pp, ';');
|
||
if (p == NULL)
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
varname = savestring (*pp, p - *pp);
|
||
|
||
*pp = p + 1;
|
||
|
||
*retp = debug_make_static_member (dhandle, name, type, varname,
|
||
visibility);
|
||
*staticsp = TRUE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
bitpos = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ',')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
bitsize = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
if (bitpos == 0 && bitsize == 0)
|
||
{
|
||
/* This can happen in two cases: (1) at least for gcc 2.4.5 or
|
||
so, it is a field which has been optimized out. The correct
|
||
stab for this case is to use VISIBILITY_IGNORE, but that is a
|
||
recent invention. (2) It is a 0-size array. For example
|
||
union { int num; char str[0]; } foo. Printing "<no value>"
|
||
for str in "p foo" is OK, since foo.str (and thus foo.str[3])
|
||
will continue to work, and a 0-size array as a whole doesn't
|
||
have any contents to print.
|
||
|
||
I suspect this probably could also happen with gcc -gstabs
|
||
(not -gstabs+) for static fields, and perhaps other C++
|
||
extensions. Hopefully few people use -gstabs with gdb, since
|
||
it is intended for dbx compatibility. */
|
||
visibility = DEBUG_VISIBILITY_IGNORE;
|
||
}
|
||
|
||
/* FIXME: gdb does some stuff here to mark fields as unpacked. */
|
||
|
||
*retp = debug_make_field (dhandle, name, type, bitpos, bitsize, visibility);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Read member function stabs info for C++ classes. The form of each member
|
||
function data is:
|
||
|
||
NAME :: TYPENUM[=type definition] ARGS : PHYSNAME ;
|
||
|
||
An example with two member functions is:
|
||
|
||
afunc1::20=##15;:i;2A.;afunc2::20:i;2A.;
|
||
|
||
For the case of overloaded operators, the format is op$::*.funcs, where
|
||
$ is the CPLUS_MARKER (usually '$'), `*' holds the place for an operator
|
||
name (such as `+=') and `.' marks the end of the operator name. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_members (void *dhandle, struct stab_handle *info,
|
||
const char *tagname, const char **pp,
|
||
const int *typenums, debug_method **retp)
|
||
{
|
||
const char *orig;
|
||
debug_method *methods;
|
||
unsigned int c;
|
||
unsigned int alloc;
|
||
|
||
*retp = NULL;
|
||
|
||
orig = *pp;
|
||
|
||
alloc = 0;
|
||
methods = NULL;
|
||
c = 0;
|
||
|
||
while (**pp != ';')
|
||
{
|
||
const char *p;
|
||
char *name;
|
||
debug_method_variant *variants;
|
||
unsigned int cvars;
|
||
unsigned int allocvars;
|
||
debug_type look_ahead_type;
|
||
|
||
p = strchr (*pp, ':');
|
||
if (p == NULL || p[1] != ':')
|
||
break;
|
||
|
||
/* FIXME: Some systems use something other than '$' here. */
|
||
if ((*pp)[0] != 'o' || (*pp)[1] != 'p' || (*pp)[2] != '$')
|
||
{
|
||
name = savestring (*pp, p - *pp);
|
||
*pp = p + 2;
|
||
}
|
||
else
|
||
{
|
||
/* This is a completely wierd case. In order to stuff in the
|
||
names that might contain colons (the usual name delimiter),
|
||
Mike Tiemann defined a different name format which is
|
||
signalled if the identifier is "op$". In that case, the
|
||
format is "op$::XXXX." where XXXX is the name. This is
|
||
used for names like "+" or "=". YUUUUUUUK! FIXME! */
|
||
*pp = p + 2;
|
||
for (p = *pp; *p != '.' && *p != '\0'; p++)
|
||
;
|
||
if (*p != '.')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
name = savestring (*pp, p - *pp);
|
||
*pp = p + 1;
|
||
}
|
||
|
||
allocvars = 10;
|
||
variants = ((debug_method_variant *)
|
||
xmalloc (allocvars * sizeof *variants));
|
||
cvars = 0;
|
||
|
||
look_ahead_type = DEBUG_TYPE_NULL;
|
||
|
||
do
|
||
{
|
||
debug_type type;
|
||
bfd_boolean stub;
|
||
char *argtypes;
|
||
enum debug_visibility visibility;
|
||
bfd_boolean constp, volatilep, staticp;
|
||
bfd_vma voffset;
|
||
debug_type context;
|
||
const char *physname;
|
||
bfd_boolean varargs;
|
||
|
||
if (look_ahead_type != DEBUG_TYPE_NULL)
|
||
{
|
||
/* g++ version 1 kludge */
|
||
type = look_ahead_type;
|
||
look_ahead_type = DEBUG_TYPE_NULL;
|
||
}
|
||
else
|
||
{
|
||
type = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
if (**pp != ':')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
++*pp;
|
||
p = strchr (*pp, ';');
|
||
if (p == NULL)
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
stub = FALSE;
|
||
if (debug_get_type_kind (dhandle, type) == DEBUG_KIND_METHOD
|
||
&& debug_get_parameter_types (dhandle, type, &varargs) == NULL)
|
||
stub = TRUE;
|
||
|
||
argtypes = savestring (*pp, p - *pp);
|
||
*pp = p + 1;
|
||
|
||
switch (**pp)
|
||
{
|
||
case '0':
|
||
visibility = DEBUG_VISIBILITY_PRIVATE;
|
||
break;
|
||
case '1':
|
||
visibility = DEBUG_VISIBILITY_PROTECTED;
|
||
break;
|
||
default:
|
||
visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
break;
|
||
}
|
||
++*pp;
|
||
|
||
constp = FALSE;
|
||
volatilep = FALSE;
|
||
switch (**pp)
|
||
{
|
||
case 'A':
|
||
/* Normal function. */
|
||
++*pp;
|
||
break;
|
||
case 'B':
|
||
/* const member function. */
|
||
constp = TRUE;
|
||
++*pp;
|
||
break;
|
||
case 'C':
|
||
/* volatile member function. */
|
||
volatilep = TRUE;
|
||
++*pp;
|
||
break;
|
||
case 'D':
|
||
/* const volatile member function. */
|
||
constp = TRUE;
|
||
volatilep = TRUE;
|
||
++*pp;
|
||
break;
|
||
case '*':
|
||
case '?':
|
||
case '.':
|
||
/* File compiled with g++ version 1; no information. */
|
||
break;
|
||
default:
|
||
warn_stab (orig, _("const/volatile indicator missing"));
|
||
break;
|
||
}
|
||
|
||
staticp = FALSE;
|
||
switch (**pp)
|
||
{
|
||
case '*':
|
||
/* virtual member function, followed by index. The sign
|
||
bit is supposedly set to distinguish
|
||
pointers-to-methods from virtual function indicies. */
|
||
++*pp;
|
||
voffset = parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
voffset &= 0x7fffffff;
|
||
|
||
if (**pp == ';' || *pp == '\0')
|
||
{
|
||
/* Must be g++ version 1. */
|
||
context = DEBUG_TYPE_NULL;
|
||
}
|
||
else
|
||
{
|
||
/* Figure out from whence this virtual function
|
||
came. It may belong to virtual function table of
|
||
one of its baseclasses. */
|
||
look_ahead_type = parse_stab_type (dhandle, info,
|
||
(const char *) NULL,
|
||
pp,
|
||
(debug_type **) NULL);
|
||
if (**pp == ':')
|
||
{
|
||
/* g++ version 1 overloaded methods. */
|
||
context = DEBUG_TYPE_NULL;
|
||
}
|
||
else
|
||
{
|
||
context = look_ahead_type;
|
||
look_ahead_type = DEBUG_TYPE_NULL;
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case '?':
|
||
/* static member function. */
|
||
++*pp;
|
||
staticp = TRUE;
|
||
voffset = 0;
|
||
context = DEBUG_TYPE_NULL;
|
||
if (strncmp (argtypes, name, strlen (name)) != 0)
|
||
stub = TRUE;
|
||
break;
|
||
|
||
default:
|
||
warn_stab (orig, "member function type missing");
|
||
voffset = 0;
|
||
context = DEBUG_TYPE_NULL;
|
||
break;
|
||
|
||
case '.':
|
||
++*pp;
|
||
voffset = 0;
|
||
context = DEBUG_TYPE_NULL;
|
||
break;
|
||
}
|
||
|
||
/* If the type is not a stub, then the argtypes string is
|
||
the physical name of the function. Otherwise the
|
||
argtypes string is the mangled form of the argument
|
||
types, and the full type and the physical name must be
|
||
extracted from them. */
|
||
if (! stub)
|
||
physname = argtypes;
|
||
else
|
||
{
|
||
debug_type class_type, return_type;
|
||
|
||
class_type = stab_find_type (dhandle, info, typenums);
|
||
if (class_type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
return_type = debug_get_return_type (dhandle, type);
|
||
if (return_type == DEBUG_TYPE_NULL)
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
type = parse_stab_argtypes (dhandle, info, class_type, name,
|
||
tagname, return_type, argtypes,
|
||
constp, volatilep, &physname);
|
||
if (type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
|
||
if (cvars + 1 >= allocvars)
|
||
{
|
||
allocvars += 10;
|
||
variants = ((debug_method_variant *)
|
||
xrealloc (variants,
|
||
allocvars * sizeof *variants));
|
||
}
|
||
|
||
if (! staticp)
|
||
variants[cvars] = debug_make_method_variant (dhandle, physname,
|
||
type, visibility,
|
||
constp, volatilep,
|
||
voffset, context);
|
||
else
|
||
variants[cvars] = debug_make_static_method_variant (dhandle,
|
||
physname,
|
||
type,
|
||
visibility,
|
||
constp,
|
||
volatilep);
|
||
if (variants[cvars] == DEBUG_METHOD_VARIANT_NULL)
|
||
return FALSE;
|
||
|
||
++cvars;
|
||
}
|
||
while (**pp != ';' && **pp != '\0');
|
||
|
||
variants[cvars] = DEBUG_METHOD_VARIANT_NULL;
|
||
|
||
if (**pp != '\0')
|
||
++*pp;
|
||
|
||
if (c + 1 >= alloc)
|
||
{
|
||
alloc += 10;
|
||
methods = ((debug_method *)
|
||
xrealloc (methods, alloc * sizeof *methods));
|
||
}
|
||
|
||
methods[c] = debug_make_method (dhandle, name, variants);
|
||
|
||
++c;
|
||
}
|
||
|
||
if (methods != NULL)
|
||
methods[c] = DEBUG_METHOD_NULL;
|
||
|
||
*retp = methods;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Parse a string representing argument types for a method. Stabs
|
||
tries to save space by packing argument types into a mangled
|
||
string. This string should give us enough information to extract
|
||
both argument types and the physical name of the function, given
|
||
the tag name. */
|
||
|
||
static debug_type
|
||
parse_stab_argtypes (void *dhandle, struct stab_handle *info,
|
||
debug_type class_type, const char *fieldname,
|
||
const char *tagname, debug_type return_type,
|
||
const char *argtypes, bfd_boolean constp,
|
||
bfd_boolean volatilep, const char **pphysname)
|
||
{
|
||
bfd_boolean is_full_physname_constructor;
|
||
bfd_boolean is_constructor;
|
||
bfd_boolean is_destructor;
|
||
debug_type *args;
|
||
bfd_boolean varargs;
|
||
unsigned int physname_len = 0;
|
||
|
||
/* Constructors are sometimes handled specially. */
|
||
is_full_physname_constructor = ((argtypes[0] == '_'
|
||
&& argtypes[1] == '_'
|
||
&& (ISDIGIT (argtypes[2])
|
||
|| argtypes[2] == 'Q'
|
||
|| argtypes[2] == 't'))
|
||
|| strncmp (argtypes, "__ct", 4) == 0);
|
||
|
||
is_constructor = (is_full_physname_constructor
|
||
|| (tagname != NULL
|
||
&& strcmp (fieldname, tagname) == 0));
|
||
is_destructor = ((argtypes[0] == '_'
|
||
&& (argtypes[1] == '$' || argtypes[1] == '.')
|
||
&& argtypes[2] == '_')
|
||
|| strncmp (argtypes, "__dt", 4) == 0);
|
||
|
||
if (is_destructor || is_full_physname_constructor)
|
||
*pphysname = argtypes;
|
||
else
|
||
{
|
||
unsigned int len;
|
||
const char *const_prefix;
|
||
const char *volatile_prefix;
|
||
char buf[20];
|
||
unsigned int mangled_name_len;
|
||
char *physname;
|
||
|
||
len = tagname == NULL ? 0 : strlen (tagname);
|
||
const_prefix = constp ? "C" : "";
|
||
volatile_prefix = volatilep ? "V" : "";
|
||
|
||
if (len == 0)
|
||
sprintf (buf, "__%s%s", const_prefix, volatile_prefix);
|
||
else if (tagname != NULL && strchr (tagname, '<') != NULL)
|
||
{
|
||
/* Template methods are fully mangled. */
|
||
sprintf (buf, "__%s%s", const_prefix, volatile_prefix);
|
||
tagname = NULL;
|
||
len = 0;
|
||
}
|
||
else
|
||
sprintf (buf, "__%s%s%d", const_prefix, volatile_prefix, len);
|
||
|
||
mangled_name_len = ((is_constructor ? 0 : strlen (fieldname))
|
||
+ strlen (buf)
|
||
+ len
|
||
+ strlen (argtypes)
|
||
+ 1);
|
||
|
||
if (fieldname[0] == 'o'
|
||
&& fieldname[1] == 'p'
|
||
&& (fieldname[2] == '$' || fieldname[2] == '.'))
|
||
{
|
||
const char *opname;
|
||
|
||
opname = cplus_mangle_opname (fieldname + 3, 0);
|
||
if (opname == NULL)
|
||
{
|
||
fprintf (stderr, _("No mangling for \"%s\"\n"), fieldname);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
mangled_name_len += strlen (opname);
|
||
physname = (char *) xmalloc (mangled_name_len);
|
||
strncpy (physname, fieldname, 3);
|
||
strcpy (physname + 3, opname);
|
||
}
|
||
else
|
||
{
|
||
physname = (char *) xmalloc (mangled_name_len);
|
||
if (is_constructor)
|
||
physname[0] = '\0';
|
||
else
|
||
strcpy (physname, fieldname);
|
||
}
|
||
|
||
physname_len = strlen (physname);
|
||
strcat (physname, buf);
|
||
if (tagname != NULL)
|
||
strcat (physname, tagname);
|
||
strcat (physname, argtypes);
|
||
|
||
*pphysname = physname;
|
||
}
|
||
|
||
if (*argtypes == '\0' || is_destructor)
|
||
{
|
||
args = (debug_type *) xmalloc (sizeof *args);
|
||
*args = NULL;
|
||
return debug_make_method_type (dhandle, return_type, class_type, args,
|
||
FALSE);
|
||
}
|
||
|
||
args = stab_demangle_argtypes (dhandle, info, *pphysname, &varargs, physname_len);
|
||
if (args == NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
return debug_make_method_type (dhandle, return_type, class_type, args,
|
||
varargs);
|
||
}
|
||
|
||
/* The tail end of stabs for C++ classes that contain a virtual function
|
||
pointer contains a tilde, a %, and a type number.
|
||
The type number refers to the base class (possibly this class itself) which
|
||
contains the vtable pointer for the current class.
|
||
|
||
This function is called when we have parsed all the method declarations,
|
||
so we can look for the vptr base class info. */
|
||
|
||
static bfd_boolean
|
||
parse_stab_tilde_field (void *dhandle, struct stab_handle *info,
|
||
const char **pp, const int *typenums,
|
||
debug_type *retvptrbase, bfd_boolean *retownvptr)
|
||
{
|
||
const char *orig;
|
||
const char *hold;
|
||
int vtypenums[2];
|
||
|
||
*retvptrbase = DEBUG_TYPE_NULL;
|
||
*retownvptr = FALSE;
|
||
|
||
orig = *pp;
|
||
|
||
/* If we are positioned at a ';', then skip it. */
|
||
if (**pp == ';')
|
||
++*pp;
|
||
|
||
if (**pp != '~')
|
||
return TRUE;
|
||
|
||
++*pp;
|
||
|
||
if (**pp == '=' || **pp == '+' || **pp == '-')
|
||
{
|
||
/* Obsolete flags that used to indicate the presence of
|
||
constructors and/or destructors. */
|
||
++*pp;
|
||
}
|
||
|
||
if (**pp != '%')
|
||
return TRUE;
|
||
|
||
++*pp;
|
||
|
||
hold = *pp;
|
||
|
||
/* The next number is the type number of the base class (possibly
|
||
our own class) which supplies the vtable for this class. */
|
||
if (! parse_stab_type_number (pp, vtypenums))
|
||
return FALSE;
|
||
|
||
if (vtypenums[0] == typenums[0]
|
||
&& vtypenums[1] == typenums[1])
|
||
*retownvptr = TRUE;
|
||
else
|
||
{
|
||
debug_type vtype;
|
||
const char *p;
|
||
|
||
*pp = hold;
|
||
|
||
vtype = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
for (p = *pp; *p != ';' && *p != '\0'; p++)
|
||
;
|
||
if (*p != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
*retvptrbase = vtype;
|
||
|
||
*pp = p + 1;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Read a definition of an array type. */
|
||
|
||
static debug_type
|
||
parse_stab_array_type (void *dhandle, struct stab_handle *info,
|
||
const char **pp, bfd_boolean stringp)
|
||
{
|
||
const char *orig;
|
||
const char *p;
|
||
int typenums[2];
|
||
debug_type index_type;
|
||
bfd_boolean adjustable;
|
||
bfd_signed_vma lower, upper;
|
||
debug_type element_type;
|
||
|
||
/* Format of an array type:
|
||
"ar<index type>;lower;upper;<array_contents_type>".
|
||
OS9000: "arlower,upper;<array_contents_type>".
|
||
|
||
Fortran adjustable arrays use Adigits or Tdigits for lower or upper;
|
||
for these, produce a type like float[][]. */
|
||
|
||
orig = *pp;
|
||
|
||
/* FIXME: gdb checks os9k_stabs here. */
|
||
|
||
/* If the index type is type 0, we take it as int. */
|
||
p = *pp;
|
||
if (! parse_stab_type_number (&p, typenums))
|
||
return DEBUG_TYPE_NULL;
|
||
if (typenums[0] == 0 && typenums[1] == 0 && **pp != '=')
|
||
{
|
||
index_type = debug_find_named_type (dhandle, "int");
|
||
if (index_type == DEBUG_TYPE_NULL)
|
||
{
|
||
index_type = debug_make_int_type (dhandle, 4, FALSE);
|
||
if (index_type == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
*pp = p;
|
||
}
|
||
else
|
||
{
|
||
index_type = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
}
|
||
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
adjustable = FALSE;
|
||
|
||
if (! ISDIGIT (**pp) && **pp != '-')
|
||
{
|
||
++*pp;
|
||
adjustable = TRUE;
|
||
}
|
||
|
||
lower = (bfd_signed_vma) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
if (! ISDIGIT (**pp) && **pp != '-')
|
||
{
|
||
++*pp;
|
||
adjustable = TRUE;
|
||
}
|
||
|
||
upper = (bfd_signed_vma) parse_number (pp, (bfd_boolean *) NULL);
|
||
if (**pp != ';')
|
||
{
|
||
bad_stab (orig);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
++*pp;
|
||
|
||
element_type = parse_stab_type (dhandle, info, (const char *) NULL, pp,
|
||
(debug_type **) NULL);
|
||
if (element_type == DEBUG_TYPE_NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (adjustable)
|
||
{
|
||
lower = 0;
|
||
upper = -1;
|
||
}
|
||
|
||
return debug_make_array_type (dhandle, element_type, index_type, lower,
|
||
upper, stringp);
|
||
}
|
||
|
||
/* This struct holds information about files we have seen using
|
||
N_BINCL. */
|
||
|
||
struct bincl_file
|
||
{
|
||
/* The next N_BINCL file. */
|
||
struct bincl_file *next;
|
||
/* The next N_BINCL on the stack. */
|
||
struct bincl_file *next_stack;
|
||
/* The file name. */
|
||
const char *name;
|
||
/* The hash value. */
|
||
bfd_vma hash;
|
||
/* The file index. */
|
||
unsigned int file;
|
||
/* The list of types defined in this file. */
|
||
struct stab_types *file_types;
|
||
};
|
||
|
||
/* Start a new N_BINCL file, pushing it onto the stack. */
|
||
|
||
static void
|
||
push_bincl (struct stab_handle *info, const char *name, bfd_vma hash)
|
||
{
|
||
struct bincl_file *n;
|
||
|
||
n = (struct bincl_file *) xmalloc (sizeof *n);
|
||
n->next = info->bincl_list;
|
||
n->next_stack = info->bincl_stack;
|
||
n->name = name;
|
||
n->hash = hash;
|
||
n->file = info->files;
|
||
n->file_types = NULL;
|
||
info->bincl_list = n;
|
||
info->bincl_stack = n;
|
||
|
||
++info->files;
|
||
info->file_types = ((struct stab_types **)
|
||
xrealloc (info->file_types,
|
||
(info->files
|
||
* sizeof *info->file_types)));
|
||
info->file_types[n->file] = NULL;
|
||
}
|
||
|
||
/* Finish an N_BINCL file, at an N_EINCL, popping the name off the
|
||
stack. */
|
||
|
||
static const char *
|
||
pop_bincl (struct stab_handle *info)
|
||
{
|
||
struct bincl_file *o;
|
||
|
||
o = info->bincl_stack;
|
||
if (o == NULL)
|
||
return info->main_filename;
|
||
info->bincl_stack = o->next_stack;
|
||
|
||
o->file_types = info->file_types[o->file];
|
||
|
||
if (info->bincl_stack == NULL)
|
||
return info->main_filename;
|
||
return info->bincl_stack->name;
|
||
}
|
||
|
||
/* Handle an N_EXCL: get the types from the corresponding N_BINCL. */
|
||
|
||
static bfd_boolean
|
||
find_excl (struct stab_handle *info, const char *name, bfd_vma hash)
|
||
{
|
||
struct bincl_file *l;
|
||
|
||
++info->files;
|
||
info->file_types = ((struct stab_types **)
|
||
xrealloc (info->file_types,
|
||
(info->files
|
||
* sizeof *info->file_types)));
|
||
|
||
for (l = info->bincl_list; l != NULL; l = l->next)
|
||
if (l->hash == hash && strcmp (l->name, name) == 0)
|
||
break;
|
||
if (l == NULL)
|
||
{
|
||
warn_stab (name, _("Undefined N_EXCL"));
|
||
info->file_types[info->files - 1] = NULL;
|
||
return TRUE;
|
||
}
|
||
|
||
info->file_types[info->files - 1] = l->file_types;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Handle a variable definition. gcc emits variable definitions for a
|
||
block before the N_LBRAC, so we must hold onto them until we see
|
||
it. The SunPRO compiler emits variable definitions after the
|
||
N_LBRAC, so we can call debug_record_variable immediately. */
|
||
|
||
static bfd_boolean
|
||
stab_record_variable (void *dhandle, struct stab_handle *info,
|
||
const char *name, debug_type type,
|
||
enum debug_var_kind kind, bfd_vma val)
|
||
{
|
||
struct stab_pending_var *v;
|
||
|
||
if ((kind == DEBUG_GLOBAL || kind == DEBUG_STATIC)
|
||
|| ! info->within_function
|
||
|| (info->gcc_compiled == 0 && info->n_opt_found))
|
||
return debug_record_variable (dhandle, name, type, kind, val);
|
||
|
||
v = (struct stab_pending_var *) xmalloc (sizeof *v);
|
||
memset (v, 0, sizeof *v);
|
||
|
||
v->next = info->pending;
|
||
v->name = name;
|
||
v->type = type;
|
||
v->kind = kind;
|
||
v->val = val;
|
||
info->pending = v;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Emit pending variable definitions. This is called after we see the
|
||
N_LBRAC that starts the block. */
|
||
|
||
static bfd_boolean
|
||
stab_emit_pending_vars (void *dhandle, struct stab_handle *info)
|
||
{
|
||
struct stab_pending_var *v;
|
||
|
||
v = info->pending;
|
||
while (v != NULL)
|
||
{
|
||
struct stab_pending_var *next;
|
||
|
||
if (! debug_record_variable (dhandle, v->name, v->type, v->kind, v->val))
|
||
return FALSE;
|
||
|
||
next = v->next;
|
||
free (v);
|
||
v = next;
|
||
}
|
||
|
||
info->pending = NULL;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Find the slot for a type in the database. */
|
||
|
||
static debug_type *
|
||
stab_find_slot (struct stab_handle *info, const int *typenums)
|
||
{
|
||
int filenum;
|
||
int index;
|
||
struct stab_types **ps;
|
||
|
||
filenum = typenums[0];
|
||
index = typenums[1];
|
||
|
||
if (filenum < 0 || (unsigned int) filenum >= info->files)
|
||
{
|
||
fprintf (stderr, _("Type file number %d out of range\n"), filenum);
|
||
return NULL;
|
||
}
|
||
if (index < 0)
|
||
{
|
||
fprintf (stderr, _("Type index number %d out of range\n"), index);
|
||
return NULL;
|
||
}
|
||
|
||
ps = info->file_types + filenum;
|
||
|
||
while (index >= STAB_TYPES_SLOTS)
|
||
{
|
||
if (*ps == NULL)
|
||
{
|
||
*ps = (struct stab_types *) xmalloc (sizeof **ps);
|
||
memset (*ps, 0, sizeof **ps);
|
||
}
|
||
ps = &(*ps)->next;
|
||
index -= STAB_TYPES_SLOTS;
|
||
}
|
||
if (*ps == NULL)
|
||
{
|
||
*ps = (struct stab_types *) xmalloc (sizeof **ps);
|
||
memset (*ps, 0, sizeof **ps);
|
||
}
|
||
|
||
return (*ps)->types + index;
|
||
}
|
||
|
||
/* Find a type given a type number. If the type has not been
|
||
allocated yet, create an indirect type. */
|
||
|
||
static debug_type
|
||
stab_find_type (void *dhandle, struct stab_handle *info, const int *typenums)
|
||
{
|
||
debug_type *slot;
|
||
|
||
if (typenums[0] == 0 && typenums[1] < 0)
|
||
{
|
||
/* A negative type number indicates an XCOFF builtin type. */
|
||
return stab_xcoff_builtin_type (dhandle, info, typenums[1]);
|
||
}
|
||
|
||
slot = stab_find_slot (info, typenums);
|
||
if (slot == NULL)
|
||
return DEBUG_TYPE_NULL;
|
||
|
||
if (*slot == DEBUG_TYPE_NULL)
|
||
return debug_make_indirect_type (dhandle, slot, (const char *) NULL);
|
||
|
||
return *slot;
|
||
}
|
||
|
||
/* Record that a given type number refers to a given type. */
|
||
|
||
static bfd_boolean
|
||
stab_record_type (void *dhandle ATTRIBUTE_UNUSED, struct stab_handle *info,
|
||
const int *typenums, debug_type type)
|
||
{
|
||
debug_type *slot;
|
||
|
||
slot = stab_find_slot (info, typenums);
|
||
if (slot == NULL)
|
||
return FALSE;
|
||
|
||
/* gdb appears to ignore type redefinitions, so we do as well. */
|
||
|
||
*slot = type;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Return an XCOFF builtin type. */
|
||
|
||
static debug_type
|
||
stab_xcoff_builtin_type (void *dhandle, struct stab_handle *info,
|
||
int typenum)
|
||
{
|
||
debug_type rettype;
|
||
const char *name;
|
||
|
||
if (typenum >= 0 || typenum < -XCOFF_TYPE_COUNT)
|
||
{
|
||
fprintf (stderr, _("Unrecognized XCOFF type %d\n"), typenum);
|
||
return DEBUG_TYPE_NULL;
|
||
}
|
||
if (info->xcoff_types[-typenum] != NULL)
|
||
return info->xcoff_types[-typenum];
|
||
|
||
switch (-typenum)
|
||
{
|
||
case 1:
|
||
/* The size of this and all the other types are fixed, defined
|
||
by the debugging format. */
|
||
name = "int";
|
||
rettype = debug_make_int_type (dhandle, 4, FALSE);
|
||
break;
|
||
case 2:
|
||
name = "char";
|
||
rettype = debug_make_int_type (dhandle, 1, FALSE);
|
||
break;
|
||
case 3:
|
||
name = "short";
|
||
rettype = debug_make_int_type (dhandle, 2, FALSE);
|
||
break;
|
||
case 4:
|
||
name = "long";
|
||
rettype = debug_make_int_type (dhandle, 4, FALSE);
|
||
break;
|
||
case 5:
|
||
name = "unsigned char";
|
||
rettype = debug_make_int_type (dhandle, 1, TRUE);
|
||
break;
|
||
case 6:
|
||
name = "signed char";
|
||
rettype = debug_make_int_type (dhandle, 1, FALSE);
|
||
break;
|
||
case 7:
|
||
name = "unsigned short";
|
||
rettype = debug_make_int_type (dhandle, 2, TRUE);
|
||
break;
|
||
case 8:
|
||
name = "unsigned int";
|
||
rettype = debug_make_int_type (dhandle, 4, TRUE);
|
||
break;
|
||
case 9:
|
||
name = "unsigned";
|
||
rettype = debug_make_int_type (dhandle, 4, TRUE);
|
||
case 10:
|
||
name = "unsigned long";
|
||
rettype = debug_make_int_type (dhandle, 4, TRUE);
|
||
break;
|
||
case 11:
|
||
name = "void";
|
||
rettype = debug_make_void_type (dhandle);
|
||
break;
|
||
case 12:
|
||
/* IEEE single precision (32 bit). */
|
||
name = "float";
|
||
rettype = debug_make_float_type (dhandle, 4);
|
||
break;
|
||
case 13:
|
||
/* IEEE double precision (64 bit). */
|
||
name = "double";
|
||
rettype = debug_make_float_type (dhandle, 8);
|
||
break;
|
||
case 14:
|
||
/* This is an IEEE double on the RS/6000, and different machines
|
||
with different sizes for "long double" should use different
|
||
negative type numbers. See stabs.texinfo. */
|
||
name = "long double";
|
||
rettype = debug_make_float_type (dhandle, 8);
|
||
break;
|
||
case 15:
|
||
name = "integer";
|
||
rettype = debug_make_int_type (dhandle, 4, FALSE);
|
||
break;
|
||
case 16:
|
||
name = "boolean";
|
||
rettype = debug_make_bool_type (dhandle, 4);
|
||
break;
|
||
case 17:
|
||
name = "short real";
|
||
rettype = debug_make_float_type (dhandle, 4);
|
||
break;
|
||
case 18:
|
||
name = "real";
|
||
rettype = debug_make_float_type (dhandle, 8);
|
||
break;
|
||
case 19:
|
||
/* FIXME */
|
||
name = "stringptr";
|
||
rettype = NULL;
|
||
break;
|
||
case 20:
|
||
/* FIXME */
|
||
name = "character";
|
||
rettype = debug_make_int_type (dhandle, 1, TRUE);
|
||
break;
|
||
case 21:
|
||
name = "logical*1";
|
||
rettype = debug_make_bool_type (dhandle, 1);
|
||
break;
|
||
case 22:
|
||
name = "logical*2";
|
||
rettype = debug_make_bool_type (dhandle, 2);
|
||
break;
|
||
case 23:
|
||
name = "logical*4";
|
||
rettype = debug_make_bool_type (dhandle, 4);
|
||
break;
|
||
case 24:
|
||
name = "logical";
|
||
rettype = debug_make_bool_type (dhandle, 4);
|
||
break;
|
||
case 25:
|
||
/* Complex type consisting of two IEEE single precision values. */
|
||
name = "complex";
|
||
rettype = debug_make_complex_type (dhandle, 8);
|
||
break;
|
||
case 26:
|
||
/* Complex type consisting of two IEEE double precision values. */
|
||
name = "double complex";
|
||
rettype = debug_make_complex_type (dhandle, 16);
|
||
break;
|
||
case 27:
|
||
name = "integer*1";
|
||
rettype = debug_make_int_type (dhandle, 1, FALSE);
|
||
break;
|
||
case 28:
|
||
name = "integer*2";
|
||
rettype = debug_make_int_type (dhandle, 2, FALSE);
|
||
break;
|
||
case 29:
|
||
name = "integer*4";
|
||
rettype = debug_make_int_type (dhandle, 4, FALSE);
|
||
break;
|
||
case 30:
|
||
/* FIXME */
|
||
name = "wchar";
|
||
rettype = debug_make_int_type (dhandle, 2, FALSE);
|
||
break;
|
||
case 31:
|
||
name = "long long";
|
||
rettype = debug_make_int_type (dhandle, 8, FALSE);
|
||
break;
|
||
case 32:
|
||
name = "unsigned long long";
|
||
rettype = debug_make_int_type (dhandle, 8, TRUE);
|
||
break;
|
||
case 33:
|
||
name = "logical*8";
|
||
rettype = debug_make_bool_type (dhandle, 8);
|
||
break;
|
||
case 34:
|
||
name = "integer*8";
|
||
rettype = debug_make_int_type (dhandle, 8, FALSE);
|
||
break;
|
||
default:
|
||
abort ();
|
||
}
|
||
|
||
rettype = debug_name_type (dhandle, name, rettype);
|
||
|
||
info->xcoff_types[-typenum] = rettype;
|
||
|
||
return rettype;
|
||
}
|
||
|
||
/* Find or create a tagged type. */
|
||
|
||
static debug_type
|
||
stab_find_tagged_type (void *dhandle, struct stab_handle *info,
|
||
const char *p, int len, enum debug_type_kind kind)
|
||
{
|
||
char *name;
|
||
debug_type dtype;
|
||
struct stab_tag *st;
|
||
|
||
name = savestring (p, len);
|
||
|
||
/* We pass DEBUG_KIND_ILLEGAL because we want all tags in the same
|
||
namespace. This is right for C, and I don't know how to handle
|
||
other languages. FIXME. */
|
||
dtype = debug_find_tagged_type (dhandle, name, DEBUG_KIND_ILLEGAL);
|
||
if (dtype != DEBUG_TYPE_NULL)
|
||
{
|
||
free (name);
|
||
return dtype;
|
||
}
|
||
|
||
/* We need to allocate an entry on the undefined tag list. */
|
||
for (st = info->tags; st != NULL; st = st->next)
|
||
{
|
||
if (st->name[0] == name[0]
|
||
&& strcmp (st->name, name) == 0)
|
||
{
|
||
if (st->kind == DEBUG_KIND_ILLEGAL)
|
||
st->kind = kind;
|
||
free (name);
|
||
break;
|
||
}
|
||
}
|
||
if (st == NULL)
|
||
{
|
||
st = (struct stab_tag *) xmalloc (sizeof *st);
|
||
memset (st, 0, sizeof *st);
|
||
|
||
st->next = info->tags;
|
||
st->name = name;
|
||
st->kind = kind;
|
||
st->slot = DEBUG_TYPE_NULL;
|
||
st->type = debug_make_indirect_type (dhandle, &st->slot, name);
|
||
info->tags = st;
|
||
}
|
||
|
||
return st->type;
|
||
}
|
||
|
||
/* In order to get the correct argument types for a stubbed method, we
|
||
need to extract the argument types from a C++ mangled string.
|
||
Since the argument types can refer back to the return type, this
|
||
means that we must demangle the entire physical name. In gdb this
|
||
is done by calling cplus_demangle and running the results back
|
||
through the C++ expression parser. Since we have no expression
|
||
parser, we must duplicate much of the work of cplus_demangle here.
|
||
|
||
We assume that GNU style demangling is used, since this is only
|
||
done for method stubs, and only g++ should output that form of
|
||
debugging information. */
|
||
|
||
/* This structure is used to hold a pointer to type information which
|
||
demangling a string. */
|
||
|
||
struct stab_demangle_typestring
|
||
{
|
||
/* The start of the type. This is not null terminated. */
|
||
const char *typestring;
|
||
/* The length of the type. */
|
||
unsigned int len;
|
||
};
|
||
|
||
/* This structure is used to hold information while demangling a
|
||
string. */
|
||
|
||
struct stab_demangle_info
|
||
{
|
||
/* The debugging information handle. */
|
||
void *dhandle;
|
||
/* The stab information handle. */
|
||
struct stab_handle *info;
|
||
/* The array of arguments we are building. */
|
||
debug_type *args;
|
||
/* Whether the method takes a variable number of arguments. */
|
||
bfd_boolean varargs;
|
||
/* The array of types we have remembered. */
|
||
struct stab_demangle_typestring *typestrings;
|
||
/* The number of typestrings. */
|
||
unsigned int typestring_count;
|
||
/* The number of typestring slots we have allocated. */
|
||
unsigned int typestring_alloc;
|
||
};
|
||
|
||
static void stab_bad_demangle (const char *);
|
||
static unsigned int stab_demangle_count (const char **);
|
||
static bfd_boolean stab_demangle_get_count (const char **, unsigned int *);
|
||
static bfd_boolean stab_demangle_prefix
|
||
(struct stab_demangle_info *, const char **, unsigned int);
|
||
static bfd_boolean stab_demangle_function_name
|
||
(struct stab_demangle_info *, const char **, const char *);
|
||
static bfd_boolean stab_demangle_signature
|
||
(struct stab_demangle_info *, const char **);
|
||
static bfd_boolean stab_demangle_qualified
|
||
(struct stab_demangle_info *, const char **, debug_type *);
|
||
static bfd_boolean stab_demangle_template
|
||
(struct stab_demangle_info *, const char **, char **);
|
||
static bfd_boolean stab_demangle_class
|
||
(struct stab_demangle_info *, const char **, const char **);
|
||
static bfd_boolean stab_demangle_args
|
||
(struct stab_demangle_info *, const char **, debug_type **, bfd_boolean *);
|
||
static bfd_boolean stab_demangle_arg
|
||
(struct stab_demangle_info *, const char **, debug_type **,
|
||
unsigned int *, unsigned int *);
|
||
static bfd_boolean stab_demangle_type
|
||
(struct stab_demangle_info *, const char **, debug_type *);
|
||
static bfd_boolean stab_demangle_fund_type
|
||
(struct stab_demangle_info *, const char **, debug_type *);
|
||
static bfd_boolean stab_demangle_remember_type
|
||
(struct stab_demangle_info *, const char *, int);
|
||
|
||
/* Warn about a bad demangling. */
|
||
|
||
static void
|
||
stab_bad_demangle (const char *s)
|
||
{
|
||
fprintf (stderr, _("bad mangled name `%s'\n"), s);
|
||
}
|
||
|
||
/* Get a count from a stab string. */
|
||
|
||
static unsigned int
|
||
stab_demangle_count (const char **pp)
|
||
{
|
||
unsigned int count;
|
||
|
||
count = 0;
|
||
while (ISDIGIT (**pp))
|
||
{
|
||
count *= 10;
|
||
count += **pp - '0';
|
||
++*pp;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
/* Require a count in a string. The count may be multiple digits, in
|
||
which case it must end in an underscore. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_get_count (const char **pp, unsigned int *pi)
|
||
{
|
||
if (! ISDIGIT (**pp))
|
||
return FALSE;
|
||
|
||
*pi = **pp - '0';
|
||
++*pp;
|
||
if (ISDIGIT (**pp))
|
||
{
|
||
unsigned int count;
|
||
const char *p;
|
||
|
||
count = *pi;
|
||
p = *pp;
|
||
do
|
||
{
|
||
count *= 10;
|
||
count += *p - '0';
|
||
++p;
|
||
}
|
||
while (ISDIGIT (*p));
|
||
if (*p == '_')
|
||
{
|
||
*pp = p + 1;
|
||
*pi = count;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* This function demangles a physical name, returning a NULL
|
||
terminated array of argument types. */
|
||
|
||
static debug_type *
|
||
stab_demangle_argtypes (void *dhandle, struct stab_handle *info,
|
||
const char *physname, bfd_boolean *pvarargs,
|
||
unsigned int physname_len)
|
||
{
|
||
struct stab_demangle_info minfo;
|
||
|
||
minfo.dhandle = dhandle;
|
||
minfo.info = info;
|
||
minfo.args = NULL;
|
||
minfo.varargs = FALSE;
|
||
minfo.typestring_alloc = 10;
|
||
minfo.typestrings = ((struct stab_demangle_typestring *)
|
||
xmalloc (minfo.typestring_alloc
|
||
* sizeof *minfo.typestrings));
|
||
minfo.typestring_count = 0;
|
||
|
||
/* cplus_demangle checks for special GNU mangled forms, but we can't
|
||
see any of them in mangled method argument types. */
|
||
|
||
if (! stab_demangle_prefix (&minfo, &physname, physname_len))
|
||
goto error_return;
|
||
|
||
if (*physname != '\0')
|
||
{
|
||
if (! stab_demangle_signature (&minfo, &physname))
|
||
goto error_return;
|
||
}
|
||
|
||
free (minfo.typestrings);
|
||
minfo.typestrings = NULL;
|
||
|
||
if (minfo.args == NULL)
|
||
fprintf (stderr, _("no argument types in mangled string\n"));
|
||
|
||
*pvarargs = minfo.varargs;
|
||
return minfo.args;
|
||
|
||
error_return:
|
||
if (minfo.typestrings != NULL)
|
||
free (minfo.typestrings);
|
||
return NULL;
|
||
}
|
||
|
||
/* Demangle the prefix of the mangled name. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_prefix (struct stab_demangle_info *minfo, const char **pp,
|
||
unsigned int physname_len)
|
||
{
|
||
const char *scan;
|
||
unsigned int i;
|
||
|
||
/* cplus_demangle checks for global constructors and destructors,
|
||
but we can't see them in mangled argument types. */
|
||
|
||
if (physname_len)
|
||
scan = *pp + physname_len;
|
||
else
|
||
{
|
||
/* Look for `__'. */
|
||
scan = *pp;
|
||
do
|
||
scan = strchr (scan, '_');
|
||
while (scan != NULL && *++scan != '_');
|
||
|
||
if (scan == NULL)
|
||
{
|
||
stab_bad_demangle (*pp);
|
||
return FALSE;
|
||
}
|
||
|
||
--scan;
|
||
|
||
/* We found `__'; move ahead to the last contiguous `__' pair. */
|
||
i = strspn (scan, "_");
|
||
if (i > 2)
|
||
scan += i - 2;
|
||
}
|
||
|
||
if (scan == *pp
|
||
&& (ISDIGIT (scan[2])
|
||
|| scan[2] == 'Q'
|
||
|| scan[2] == 't'))
|
||
{
|
||
/* This is a GNU style constructor name. */
|
||
*pp = scan + 2;
|
||
return TRUE;
|
||
}
|
||
else if (scan == *pp
|
||
&& ! ISDIGIT (scan[2])
|
||
&& scan[2] != 't')
|
||
{
|
||
/* Look for the `__' that separates the prefix from the
|
||
signature. */
|
||
while (*scan == '_')
|
||
++scan;
|
||
scan = strstr (scan, "__");
|
||
if (scan == NULL || scan[2] == '\0')
|
||
{
|
||
stab_bad_demangle (*pp);
|
||
return FALSE;
|
||
}
|
||
|
||
return stab_demangle_function_name (minfo, pp, scan);
|
||
}
|
||
else if (scan[2] != '\0')
|
||
{
|
||
/* The name doesn't start with `__', but it does contain `__'. */
|
||
return stab_demangle_function_name (minfo, pp, scan);
|
||
}
|
||
else
|
||
{
|
||
stab_bad_demangle (*pp);
|
||
return FALSE;
|
||
}
|
||
/*NOTREACHED*/
|
||
}
|
||
|
||
/* Demangle a function name prefix. The scan argument points to the
|
||
double underscore which separates the function name from the
|
||
signature. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_function_name (struct stab_demangle_info *minfo,
|
||
const char **pp, const char *scan)
|
||
{
|
||
const char *name;
|
||
|
||
/* The string from *pp to scan is the name of the function. We
|
||
don't care about the name, since we just looking for argument
|
||
types. However, for conversion operators, the name may include a
|
||
type which we must remember in order to handle backreferences. */
|
||
|
||
name = *pp;
|
||
*pp = scan + 2;
|
||
|
||
if (*pp - name >= 5
|
||
&& strncmp (name, "type", 4) == 0
|
||
&& (name[4] == '$' || name[4] == '.'))
|
||
{
|
||
const char *tem;
|
||
|
||
/* This is a type conversion operator. */
|
||
tem = name + 5;
|
||
if (! stab_demangle_type (minfo, &tem, (debug_type *) NULL))
|
||
return FALSE;
|
||
}
|
||
else if (name[0] == '_'
|
||
&& name[1] == '_'
|
||
&& name[2] == 'o'
|
||
&& name[3] == 'p')
|
||
{
|
||
const char *tem;
|
||
|
||
/* This is a type conversion operator. */
|
||
tem = name + 4;
|
||
if (! stab_demangle_type (minfo, &tem, (debug_type *) NULL))
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle the signature. This is where the argument types are
|
||
found. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_signature (struct stab_demangle_info *minfo, const char **pp)
|
||
{
|
||
const char *orig;
|
||
bfd_boolean expect_func, func_done;
|
||
const char *hold;
|
||
|
||
orig = *pp;
|
||
|
||
expect_func = FALSE;
|
||
func_done = FALSE;
|
||
hold = NULL;
|
||
|
||
while (**pp != '\0')
|
||
{
|
||
switch (**pp)
|
||
{
|
||
case 'Q':
|
||
hold = *pp;
|
||
if (! stab_demangle_qualified (minfo, pp, (debug_type *) NULL)
|
||
|| ! stab_demangle_remember_type (minfo, hold, *pp - hold))
|
||
return FALSE;
|
||
expect_func = TRUE;
|
||
hold = NULL;
|
||
break;
|
||
|
||
case 'S':
|
||
/* Static member function. FIXME: Can this happen? */
|
||
if (hold == NULL)
|
||
hold = *pp;
|
||
++*pp;
|
||
break;
|
||
|
||
case 'C':
|
||
/* Const member function. */
|
||
if (hold == NULL)
|
||
hold = *pp;
|
||
++*pp;
|
||
break;
|
||
|
||
case '0': case '1': case '2': case '3': case '4':
|
||
case '5': case '6': case '7': case '8': case '9':
|
||
if (hold == NULL)
|
||
hold = *pp;
|
||
if (! stab_demangle_class (minfo, pp, (const char **) NULL)
|
||
|| ! stab_demangle_remember_type (minfo, hold, *pp - hold))
|
||
return FALSE;
|
||
expect_func = TRUE;
|
||
hold = NULL;
|
||
break;
|
||
|
||
case 'F':
|
||
/* Function. I don't know if this actually happens with g++
|
||
output. */
|
||
hold = NULL;
|
||
func_done = TRUE;
|
||
++*pp;
|
||
if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 't':
|
||
/* Template. */
|
||
if (hold == NULL)
|
||
hold = *pp;
|
||
if (! stab_demangle_template (minfo, pp, (char **) NULL)
|
||
|| ! stab_demangle_remember_type (minfo, hold, *pp - hold))
|
||
return FALSE;
|
||
hold = NULL;
|
||
expect_func = TRUE;
|
||
break;
|
||
|
||
case '_':
|
||
/* At the outermost level, we cannot have a return type
|
||
specified, so if we run into another '_' at this point we
|
||
are dealing with a mangled name that is either bogus, or
|
||
has been mangled by some algorithm we don't know how to
|
||
deal with. So just reject the entire demangling. */
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
|
||
default:
|
||
/* Assume we have stumbled onto the first outermost function
|
||
argument token, and start processing args. */
|
||
func_done = TRUE;
|
||
if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
if (expect_func)
|
||
{
|
||
func_done = TRUE;
|
||
if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs))
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (! func_done)
|
||
{
|
||
/* With GNU style demangling, bar__3foo is 'foo::bar(void)', and
|
||
bar__3fooi is 'foo::bar(int)'. We get here when we find the
|
||
first case, and need to ensure that the '(void)' gets added
|
||
to the current declp. */
|
||
if (! stab_demangle_args (minfo, pp, &minfo->args, &minfo->varargs))
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle a qualified name, such as "Q25Outer5Inner" which is the
|
||
mangled form of "Outer::Inner". */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_qualified (struct stab_demangle_info *minfo, const char **pp,
|
||
debug_type *ptype)
|
||
{
|
||
const char *orig;
|
||
const char *p;
|
||
unsigned int qualifiers;
|
||
debug_type context;
|
||
|
||
orig = *pp;
|
||
|
||
switch ((*pp)[1])
|
||
{
|
||
case '_':
|
||
/* GNU mangled name with more than 9 classes. The count is
|
||
preceded by an underscore (to distinguish it from the <= 9
|
||
case) and followed by an underscore. */
|
||
p = *pp + 2;
|
||
if (! ISDIGIT (*p) || *p == '0')
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
qualifiers = atoi (p);
|
||
while (ISDIGIT (*p))
|
||
++p;
|
||
if (*p != '_')
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
*pp = p + 1;
|
||
break;
|
||
|
||
case '1': case '2': case '3': case '4': case '5':
|
||
case '6': case '7': case '8': case '9':
|
||
qualifiers = (*pp)[1] - '0';
|
||
/* Skip an optional underscore after the count. */
|
||
if ((*pp)[2] == '_')
|
||
++*pp;
|
||
*pp += 2;
|
||
break;
|
||
|
||
case '0':
|
||
default:
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
context = DEBUG_TYPE_NULL;
|
||
|
||
/* Pick off the names. */
|
||
while (qualifiers-- > 0)
|
||
{
|
||
if (**pp == '_')
|
||
++*pp;
|
||
if (**pp == 't')
|
||
{
|
||
char *name;
|
||
|
||
if (! stab_demangle_template (minfo, pp,
|
||
ptype != NULL ? &name : NULL))
|
||
return FALSE;
|
||
|
||
if (ptype != NULL)
|
||
{
|
||
context = stab_find_tagged_type (minfo->dhandle, minfo->info,
|
||
name, strlen (name),
|
||
DEBUG_KIND_CLASS);
|
||
free (name);
|
||
if (context == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
unsigned int len;
|
||
|
||
len = stab_demangle_count (pp);
|
||
if (strlen (*pp) < len)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
if (ptype != NULL)
|
||
{
|
||
const debug_field *fields;
|
||
|
||
fields = NULL;
|
||
if (context != DEBUG_TYPE_NULL)
|
||
fields = debug_get_fields (minfo->dhandle, context);
|
||
|
||
context = DEBUG_TYPE_NULL;
|
||
|
||
if (fields != NULL)
|
||
{
|
||
char *name;
|
||
|
||
/* Try to find the type by looking through the
|
||
fields of context until we find a field with the
|
||
same type. This ought to work for a class
|
||
defined within a class, but it won't work for,
|
||
e.g., an enum defined within a class. stabs does
|
||
not give us enough information to figure out the
|
||
latter case. */
|
||
|
||
name = savestring (*pp, len);
|
||
|
||
for (; *fields != DEBUG_FIELD_NULL; fields++)
|
||
{
|
||
debug_type ft;
|
||
const char *dn;
|
||
|
||
ft = debug_get_field_type (minfo->dhandle, *fields);
|
||
if (ft == NULL)
|
||
return FALSE;
|
||
dn = debug_get_type_name (minfo->dhandle, ft);
|
||
if (dn != NULL && strcmp (dn, name) == 0)
|
||
{
|
||
context = ft;
|
||
break;
|
||
}
|
||
}
|
||
|
||
free (name);
|
||
}
|
||
|
||
if (context == DEBUG_TYPE_NULL)
|
||
{
|
||
/* We have to fall back on finding the type by name.
|
||
If there are more types to come, then this must
|
||
be a class. Otherwise, it could be anything. */
|
||
|
||
if (qualifiers == 0)
|
||
{
|
||
char *name;
|
||
|
||
name = savestring (*pp, len);
|
||
context = debug_find_named_type (minfo->dhandle,
|
||
name);
|
||
free (name);
|
||
}
|
||
|
||
if (context == DEBUG_TYPE_NULL)
|
||
{
|
||
context = stab_find_tagged_type (minfo->dhandle,
|
||
minfo->info,
|
||
*pp, len,
|
||
(qualifiers == 0
|
||
? DEBUG_KIND_ILLEGAL
|
||
: DEBUG_KIND_CLASS));
|
||
if (context == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
*pp += len;
|
||
}
|
||
}
|
||
|
||
if (ptype != NULL)
|
||
*ptype = context;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle a template. If PNAME is not NULL, this sets *PNAME to a
|
||
string representation of the template. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_template (struct stab_demangle_info *minfo, const char **pp,
|
||
char **pname)
|
||
{
|
||
const char *orig;
|
||
unsigned int r, i;
|
||
|
||
orig = *pp;
|
||
|
||
++*pp;
|
||
|
||
/* Skip the template name. */
|
||
r = stab_demangle_count (pp);
|
||
if (r == 0 || strlen (*pp) < r)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
*pp += r;
|
||
|
||
/* Get the size of the parameter list. */
|
||
if (stab_demangle_get_count (pp, &r) == 0)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
for (i = 0; i < r; i++)
|
||
{
|
||
if (**pp == 'Z')
|
||
{
|
||
/* This is a type parameter. */
|
||
++*pp;
|
||
if (! stab_demangle_type (minfo, pp, (debug_type *) NULL))
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
const char *old_p;
|
||
bfd_boolean pointerp, realp, integralp, charp, boolp;
|
||
bfd_boolean done;
|
||
|
||
old_p = *pp;
|
||
pointerp = FALSE;
|
||
realp = FALSE;
|
||
integralp = FALSE;
|
||
charp = FALSE;
|
||
boolp = FALSE;
|
||
done = FALSE;
|
||
|
||
/* This is a value parameter. */
|
||
|
||
if (! stab_demangle_type (minfo, pp, (debug_type *) NULL))
|
||
return FALSE;
|
||
|
||
while (*old_p != '\0' && ! done)
|
||
{
|
||
switch (*old_p)
|
||
{
|
||
case 'P':
|
||
case 'p':
|
||
case 'R':
|
||
pointerp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
case 'C': /* Const. */
|
||
case 'S': /* Signed. */
|
||
case 'U': /* Unsigned. */
|
||
case 'V': /* Volatile. */
|
||
case 'F': /* Function. */
|
||
case 'M': /* Member function. */
|
||
case 'O': /* ??? */
|
||
++old_p;
|
||
break;
|
||
case 'Q': /* Qualified name. */
|
||
integralp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
case 'T': /* Remembered type. */
|
||
abort ();
|
||
case 'v': /* Void. */
|
||
abort ();
|
||
case 'x': /* Long long. */
|
||
case 'l': /* Long. */
|
||
case 'i': /* Int. */
|
||
case 's': /* Short. */
|
||
case 'w': /* Wchar_t. */
|
||
integralp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
case 'b': /* Bool. */
|
||
boolp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
case 'c': /* Char. */
|
||
charp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
case 'r': /* Long double. */
|
||
case 'd': /* Double. */
|
||
case 'f': /* Float. */
|
||
realp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
default:
|
||
/* Assume it's a user defined integral type. */
|
||
integralp = TRUE;
|
||
done = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (integralp)
|
||
{
|
||
if (**pp == 'm')
|
||
++*pp;
|
||
while (ISDIGIT (**pp))
|
||
++*pp;
|
||
}
|
||
else if (charp)
|
||
{
|
||
unsigned int val;
|
||
|
||
if (**pp == 'm')
|
||
++*pp;
|
||
val = stab_demangle_count (pp);
|
||
if (val == 0)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
}
|
||
else if (boolp)
|
||
{
|
||
unsigned int val;
|
||
|
||
val = stab_demangle_count (pp);
|
||
if (val != 0 && val != 1)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
}
|
||
else if (realp)
|
||
{
|
||
if (**pp == 'm')
|
||
++*pp;
|
||
while (ISDIGIT (**pp))
|
||
++*pp;
|
||
if (**pp == '.')
|
||
{
|
||
++*pp;
|
||
while (ISDIGIT (**pp))
|
||
++*pp;
|
||
}
|
||
if (**pp == 'e')
|
||
{
|
||
++*pp;
|
||
while (ISDIGIT (**pp))
|
||
++*pp;
|
||
}
|
||
}
|
||
else if (pointerp)
|
||
{
|
||
unsigned int len;
|
||
|
||
if (! stab_demangle_get_count (pp, &len))
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
*pp += len;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* We can translate this to a string fairly easily by invoking the
|
||
regular demangling routine. */
|
||
if (pname != NULL)
|
||
{
|
||
char *s1, *s2, *s3, *s4 = NULL;
|
||
char *from, *to;
|
||
|
||
s1 = savestring (orig, *pp - orig);
|
||
|
||
s2 = concat ("NoSuchStrinG__", s1, (const char *) NULL);
|
||
|
||
free (s1);
|
||
|
||
s3 = cplus_demangle (s2, DMGL_ANSI);
|
||
|
||
free (s2);
|
||
|
||
if (s3 != NULL)
|
||
s4 = strstr (s3, "::NoSuchStrinG");
|
||
if (s3 == NULL || s4 == NULL)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
if (s3 != NULL)
|
||
free (s3);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Eliminating all spaces, except those between > characters,
|
||
makes it more likely that the demangled name will match the
|
||
name which g++ used as the structure name. */
|
||
for (from = to = s3; from != s4; ++from)
|
||
if (*from != ' '
|
||
|| (from[1] == '>' && from > s3 && from[-1] == '>'))
|
||
*to++ = *from;
|
||
|
||
*pname = savestring (s3, to - s3);
|
||
|
||
free (s3);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle a class name. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_class (struct stab_demangle_info *minfo ATTRIBUTE_UNUSED,
|
||
const char **pp, const char **pstart)
|
||
{
|
||
const char *orig;
|
||
unsigned int n;
|
||
|
||
orig = *pp;
|
||
|
||
n = stab_demangle_count (pp);
|
||
if (strlen (*pp) < n)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
if (pstart != NULL)
|
||
*pstart = *pp;
|
||
|
||
*pp += n;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle function arguments. If the pargs argument is not NULL, it
|
||
is set to a NULL terminated array holding the arguments. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_args (struct stab_demangle_info *minfo, const char **pp,
|
||
debug_type **pargs, bfd_boolean *pvarargs)
|
||
{
|
||
const char *orig;
|
||
unsigned int alloc, count;
|
||
|
||
orig = *pp;
|
||
|
||
alloc = 10;
|
||
if (pargs != NULL)
|
||
{
|
||
*pargs = (debug_type *) xmalloc (alloc * sizeof **pargs);
|
||
*pvarargs = FALSE;
|
||
}
|
||
count = 0;
|
||
|
||
while (**pp != '_' && **pp != '\0' && **pp != 'e')
|
||
{
|
||
if (**pp == 'N' || **pp == 'T')
|
||
{
|
||
char temptype;
|
||
unsigned int r, t;
|
||
|
||
temptype = **pp;
|
||
++*pp;
|
||
|
||
if (temptype == 'T')
|
||
r = 1;
|
||
else
|
||
{
|
||
if (! stab_demangle_get_count (pp, &r))
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (! stab_demangle_get_count (pp, &t))
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
if (t >= minfo->typestring_count)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
while (r-- > 0)
|
||
{
|
||
const char *tem;
|
||
|
||
tem = minfo->typestrings[t].typestring;
|
||
if (! stab_demangle_arg (minfo, &tem, pargs, &count, &alloc))
|
||
return FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (! stab_demangle_arg (minfo, pp, pargs, &count, &alloc))
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (pargs != NULL)
|
||
(*pargs)[count] = DEBUG_TYPE_NULL;
|
||
|
||
if (**pp == 'e')
|
||
{
|
||
if (pargs != NULL)
|
||
*pvarargs = TRUE;
|
||
++*pp;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle a single argument. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_arg (struct stab_demangle_info *minfo, const char **pp,
|
||
debug_type **pargs, unsigned int *pcount,
|
||
unsigned int *palloc)
|
||
{
|
||
const char *start;
|
||
debug_type type;
|
||
|
||
start = *pp;
|
||
if (! stab_demangle_type (minfo, pp,
|
||
pargs == NULL ? (debug_type *) NULL : &type)
|
||
|| ! stab_demangle_remember_type (minfo, start, *pp - start))
|
||
return FALSE;
|
||
|
||
if (pargs != NULL)
|
||
{
|
||
if (type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
|
||
if (*pcount + 1 >= *palloc)
|
||
{
|
||
*palloc += 10;
|
||
*pargs = ((debug_type *)
|
||
xrealloc (*pargs, *palloc * sizeof **pargs));
|
||
}
|
||
(*pargs)[*pcount] = type;
|
||
++*pcount;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle a type. If the ptype argument is not NULL, *ptype is set
|
||
to the newly allocated type. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_type (struct stab_demangle_info *minfo, const char **pp,
|
||
debug_type *ptype)
|
||
{
|
||
const char *orig;
|
||
|
||
orig = *pp;
|
||
|
||
switch (**pp)
|
||
{
|
||
case 'P':
|
||
case 'p':
|
||
/* A pointer type. */
|
||
++*pp;
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
*ptype = debug_make_pointer_type (minfo->dhandle, *ptype);
|
||
break;
|
||
|
||
case 'R':
|
||
/* A reference type. */
|
||
++*pp;
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
*ptype = debug_make_reference_type (minfo->dhandle, *ptype);
|
||
break;
|
||
|
||
case 'A':
|
||
/* An array. */
|
||
{
|
||
unsigned long high;
|
||
|
||
++*pp;
|
||
high = 0;
|
||
while (**pp != '\0' && **pp != '_')
|
||
{
|
||
if (! ISDIGIT (**pp))
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
high *= 10;
|
||
high += **pp - '0';
|
||
++*pp;
|
||
}
|
||
if (**pp != '_')
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
{
|
||
debug_type int_type;
|
||
|
||
int_type = debug_find_named_type (minfo->dhandle, "int");
|
||
if (int_type == NULL)
|
||
int_type = debug_make_int_type (minfo->dhandle, 4, FALSE);
|
||
*ptype = debug_make_array_type (minfo->dhandle, *ptype, int_type,
|
||
0, high, FALSE);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'T':
|
||
/* A back reference to a remembered type. */
|
||
{
|
||
unsigned int i;
|
||
const char *p;
|
||
|
||
++*pp;
|
||
if (! stab_demangle_get_count (pp, &i))
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
if (i >= minfo->typestring_count)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
p = minfo->typestrings[i].typestring;
|
||
if (! stab_demangle_type (minfo, &p, ptype))
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
case 'F':
|
||
/* A function. */
|
||
{
|
||
debug_type *args;
|
||
bfd_boolean varargs;
|
||
|
||
++*pp;
|
||
if (! stab_demangle_args (minfo, pp,
|
||
(ptype == NULL
|
||
? (debug_type **) NULL
|
||
: &args),
|
||
(ptype == NULL
|
||
? (bfd_boolean *) NULL
|
||
: &varargs)))
|
||
return FALSE;
|
||
if (**pp != '_')
|
||
{
|
||
/* cplus_demangle will accept a function without a return
|
||
type, but I don't know when that will happen, or what
|
||
to do if it does. */
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
*ptype = debug_make_function_type (minfo->dhandle, *ptype, args,
|
||
varargs);
|
||
|
||
}
|
||
break;
|
||
|
||
case 'M':
|
||
case 'O':
|
||
{
|
||
bfd_boolean memberp, constp, volatilep;
|
||
debug_type class_type = DEBUG_TYPE_NULL;
|
||
debug_type *args;
|
||
bfd_boolean varargs;
|
||
unsigned int n;
|
||
const char *name;
|
||
|
||
memberp = **pp == 'M';
|
||
constp = FALSE;
|
||
volatilep = FALSE;
|
||
args = NULL;
|
||
varargs = FALSE;
|
||
|
||
++*pp;
|
||
if (ISDIGIT (**pp))
|
||
{
|
||
n = stab_demangle_count (pp);
|
||
if (strlen (*pp) < n)
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
name = *pp;
|
||
*pp += n;
|
||
|
||
if (ptype != NULL)
|
||
{
|
||
class_type = stab_find_tagged_type (minfo->dhandle,
|
||
minfo->info,
|
||
name, (int) n,
|
||
DEBUG_KIND_CLASS);
|
||
if (class_type == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
}
|
||
else if (**pp == 'Q')
|
||
{
|
||
if (! stab_demangle_qualified (minfo, pp,
|
||
(ptype == NULL
|
||
? (debug_type *) NULL
|
||
: &class_type)))
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
if (memberp)
|
||
{
|
||
if (**pp == 'C')
|
||
{
|
||
constp = TRUE;
|
||
++*pp;
|
||
}
|
||
else if (**pp == 'V')
|
||
{
|
||
volatilep = TRUE;
|
||
++*pp;
|
||
}
|
||
if (**pp != 'F')
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
if (! stab_demangle_args (minfo, pp,
|
||
(ptype == NULL
|
||
? (debug_type **) NULL
|
||
: &args),
|
||
(ptype == NULL
|
||
? (bfd_boolean *) NULL
|
||
: &varargs)))
|
||
return FALSE;
|
||
}
|
||
|
||
if (**pp != '_')
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
++*pp;
|
||
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
|
||
if (ptype != NULL)
|
||
{
|
||
if (! memberp)
|
||
*ptype = debug_make_offset_type (minfo->dhandle, class_type,
|
||
*ptype);
|
||
else
|
||
{
|
||
/* FIXME: We have no way to record constp or
|
||
volatilep. */
|
||
*ptype = debug_make_method_type (minfo->dhandle, *ptype,
|
||
class_type, args, varargs);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'G':
|
||
++*pp;
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
break;
|
||
|
||
case 'C':
|
||
++*pp;
|
||
if (! stab_demangle_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
*ptype = debug_make_const_type (minfo->dhandle, *ptype);
|
||
break;
|
||
|
||
case 'Q':
|
||
{
|
||
const char *hold;
|
||
|
||
hold = *pp;
|
||
if (! stab_demangle_qualified (minfo, pp, ptype))
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
if (! stab_demangle_fund_type (minfo, pp, ptype))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Demangle a fundamental type. If the ptype argument is not NULL,
|
||
*ptype is set to the newly allocated type. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_fund_type (struct stab_demangle_info *minfo, const char **pp,
|
||
debug_type *ptype)
|
||
{
|
||
const char *orig;
|
||
bfd_boolean constp, volatilep, unsignedp, signedp;
|
||
bfd_boolean done;
|
||
|
||
orig = *pp;
|
||
|
||
constp = FALSE;
|
||
volatilep = FALSE;
|
||
unsignedp = FALSE;
|
||
signedp = FALSE;
|
||
|
||
done = FALSE;
|
||
while (! done)
|
||
{
|
||
switch (**pp)
|
||
{
|
||
case 'C':
|
||
constp = TRUE;
|
||
++*pp;
|
||
break;
|
||
|
||
case 'U':
|
||
unsignedp = TRUE;
|
||
++*pp;
|
||
break;
|
||
|
||
case 'S':
|
||
signedp = TRUE;
|
||
++*pp;
|
||
break;
|
||
|
||
case 'V':
|
||
volatilep = TRUE;
|
||
++*pp;
|
||
break;
|
||
|
||
default:
|
||
done = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
switch (**pp)
|
||
{
|
||
case '\0':
|
||
case '_':
|
||
/* cplus_demangle permits this, but I don't know what it means. */
|
||
stab_bad_demangle (orig);
|
||
break;
|
||
|
||
case 'v': /* void */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle, "void");
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_void_type (minfo->dhandle);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'x': /* long long */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle,
|
||
(unsignedp
|
||
? "long long unsigned int"
|
||
: "long long int"));
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_int_type (minfo->dhandle, 8, unsignedp);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'l': /* long */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle,
|
||
(unsignedp
|
||
? "long unsigned int"
|
||
: "long int"));
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_int_type (minfo->dhandle, 4, unsignedp);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'i': /* int */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle,
|
||
(unsignedp
|
||
? "unsigned int"
|
||
: "int"));
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_int_type (minfo->dhandle, 4, unsignedp);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 's': /* short */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle,
|
||
(unsignedp
|
||
? "short unsigned int"
|
||
: "short int"));
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_int_type (minfo->dhandle, 2, unsignedp);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'b': /* bool */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle, "bool");
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_bool_type (minfo->dhandle, 4);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'c': /* char */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle,
|
||
(unsignedp
|
||
? "unsigned char"
|
||
: (signedp
|
||
? "signed char"
|
||
: "char")));
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_int_type (minfo->dhandle, 1, unsignedp);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'w': /* wchar_t */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle, "__wchar_t");
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_int_type (minfo->dhandle, 2, TRUE);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'r': /* long double */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle, "long double");
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_float_type (minfo->dhandle, 8);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'd': /* double */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle, "double");
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_float_type (minfo->dhandle, 8);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'f': /* float */
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = debug_find_named_type (minfo->dhandle, "float");
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
*ptype = debug_make_float_type (minfo->dhandle, 4);
|
||
}
|
||
++*pp;
|
||
break;
|
||
|
||
case 'G':
|
||
++*pp;
|
||
if (! ISDIGIT (**pp))
|
||
{
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
/* Fall through. */
|
||
case '0': case '1': case '2': case '3': case '4':
|
||
case '5': case '6': case '7': case '8': case '9':
|
||
{
|
||
const char *hold;
|
||
|
||
if (! stab_demangle_class (minfo, pp, &hold))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
{
|
||
char *name;
|
||
|
||
name = savestring (hold, *pp - hold);
|
||
*ptype = debug_find_named_type (minfo->dhandle, name);
|
||
free (name);
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
{
|
||
/* FIXME: It is probably incorrect to assume that
|
||
undefined types are tagged types. */
|
||
*ptype = stab_find_tagged_type (minfo->dhandle, minfo->info,
|
||
hold, *pp - hold,
|
||
DEBUG_KIND_ILLEGAL);
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 't':
|
||
{
|
||
char *name;
|
||
|
||
if (! stab_demangle_template (minfo, pp,
|
||
ptype != NULL ? &name : NULL))
|
||
return FALSE;
|
||
if (ptype != NULL)
|
||
{
|
||
*ptype = stab_find_tagged_type (minfo->dhandle, minfo->info,
|
||
name, strlen (name),
|
||
DEBUG_KIND_CLASS);
|
||
free (name);
|
||
if (*ptype == DEBUG_TYPE_NULL)
|
||
return FALSE;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
stab_bad_demangle (orig);
|
||
return FALSE;
|
||
}
|
||
|
||
if (ptype != NULL)
|
||
{
|
||
if (constp)
|
||
*ptype = debug_make_const_type (minfo->dhandle, *ptype);
|
||
if (volatilep)
|
||
*ptype = debug_make_volatile_type (minfo->dhandle, *ptype);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Remember a type string in a demangled string. */
|
||
|
||
static bfd_boolean
|
||
stab_demangle_remember_type (struct stab_demangle_info *minfo,
|
||
const char *p, int len)
|
||
{
|
||
if (minfo->typestring_count >= minfo->typestring_alloc)
|
||
{
|
||
minfo->typestring_alloc += 10;
|
||
minfo->typestrings = ((struct stab_demangle_typestring *)
|
||
xrealloc (minfo->typestrings,
|
||
(minfo->typestring_alloc
|
||
* sizeof *minfo->typestrings)));
|
||
}
|
||
|
||
minfo->typestrings[minfo->typestring_count].typestring = p;
|
||
minfo->typestrings[minfo->typestring_count].len = (unsigned int) len;
|
||
++minfo->typestring_count;
|
||
|
||
return TRUE;
|
||
}
|