e1c145993e
a generic printer. * budbg.h, debug.c, debug.h, prdbg.c, rddbg.c, stabs.c: New files. * objdump.c: Include "debug.h" and "budbg.h". (dump_debugging): New global variable. (usage): Mention --debugging. (long_options): Add "debugging". (display_bfd): Handle --debugging. * Makefile.in (OBJDUMP_OBJS): New variable. ($(OBJDUMP_PROG)): Use $(OBJDUMP_OBJS). * binutils.texi, objdump.1: Document --debugging.
3174 lines
75 KiB
C
3174 lines
75 KiB
C
/* stabs.c -- Parse stabs debugging information
|
|
Copyright (C) 1995 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 <ctype.h>
|
|
|
|
#include "bfd.h"
|
|
#include "bucomm.h"
|
|
#include "libiberty.h"
|
|
#include "debug.h"
|
|
#include "budbg.h"
|
|
|
|
/* Meaningless definition needs by aout64.h. FIXME. */
|
|
#define BYTES_IN_WORD 4
|
|
|
|
#include "aout/aout64.h"
|
|
#include "aout/stab_gnu.h"
|
|
|
|
/* The 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 type of the last stab symbol, so that we can detect N_SO
|
|
pairs. */
|
|
int last_type;
|
|
/* The offset of the start of the function, so that we can handle
|
|
function relative 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. */
|
|
boolean n_opt_found;
|
|
/* The main file name. */
|
|
char *main_filename;
|
|
/* A stack of N_BINCL files. */
|
|
struct bincl_file *bincl_stack;
|
|
/* Whether we are inside a function or not. */
|
|
boolean within_function;
|
|
/* 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;
|
|
};
|
|
|
|
/* 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 PARAMS ((const char *, int));
|
|
static bfd_vma parse_number PARAMS ((const char **, boolean *));
|
|
static void bad_stab PARAMS ((const char *));
|
|
static void warn_stab PARAMS ((const char *, const char *));
|
|
static boolean parse_stab_string
|
|
PARAMS ((PTR, struct stab_handle *, int, int, bfd_vma, const char *));
|
|
static debug_type parse_stab_type
|
|
PARAMS ((PTR, struct stab_handle *, const char **, debug_type **));
|
|
static boolean parse_stab_type_number
|
|
PARAMS ((const char **, int *));
|
|
static debug_type parse_stab_range_type
|
|
PARAMS ((PTR, struct stab_handle *, const char **, const int *));
|
|
static debug_type parse_stab_sun_builtin_type PARAMS ((PTR, const char **));
|
|
static debug_type parse_stab_sun_floating_type
|
|
PARAMS ((PTR, const char **));
|
|
static debug_type parse_stab_enum_type PARAMS ((PTR, const char **));
|
|
static debug_type parse_stab_struct_type
|
|
PARAMS ((PTR, struct stab_handle *, const char **, boolean, const int *));
|
|
static boolean parse_stab_baseclasses
|
|
PARAMS ((PTR, struct stab_handle *, const char **, debug_baseclass **));
|
|
static boolean parse_stab_struct_fields
|
|
PARAMS ((PTR, struct stab_handle *, const char **, debug_field **,
|
|
boolean *));
|
|
static boolean parse_stab_cpp_abbrev
|
|
PARAMS ((PTR, struct stab_handle *, const char **, debug_field *));
|
|
static boolean parse_stab_one_struct_field
|
|
PARAMS ((PTR, struct stab_handle *, const char **, const char *,
|
|
debug_field *, boolean *));
|
|
static boolean parse_stab_members
|
|
PARAMS ((PTR, struct stab_handle *, const char **, debug_method **));
|
|
static boolean parse_stab_tilde_field
|
|
PARAMS ((PTR, struct stab_handle *, const char **, const int *,
|
|
debug_type *, boolean *));
|
|
static debug_type parse_stab_array_type
|
|
PARAMS ((PTR, struct stab_handle *, const char **, boolean));
|
|
static void push_bincl PARAMS ((struct stab_handle *, const char *));
|
|
static const char *pop_bincl PARAMS ((struct stab_handle *));
|
|
static boolean stab_record_variable
|
|
PARAMS ((PTR, struct stab_handle *, const char *, debug_type,
|
|
enum debug_var_kind, bfd_vma));
|
|
static boolean stab_emit_pending_vars PARAMS ((PTR, struct stab_handle *));
|
|
static debug_type *stab_find_slot
|
|
PARAMS ((struct stab_handle *, const int *));
|
|
static debug_type stab_find_type
|
|
PARAMS ((PTR, struct stab_handle *, const int *));
|
|
static boolean stab_record_type
|
|
PARAMS ((PTR, struct stab_handle *, const int *, debug_type));
|
|
static debug_type stab_xcoff_builtin_type
|
|
PARAMS ((PTR, struct stab_handle *, int));
|
|
|
|
/* Save a string in memory. */
|
|
|
|
static char *
|
|
savestring (start, len)
|
|
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 (pp, poverflow)
|
|
const char **pp;
|
|
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)
|
|
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;
|
|
boolean neg;
|
|
int base;
|
|
bfd_vma over, lastdig;
|
|
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 ((unsigned char) d))
|
|
d -= '0';
|
|
else if (isupper ((unsigned char) d))
|
|
d -= 'A';
|
|
else if (islower ((unsigned char) 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 (p)
|
|
const char *p;
|
|
{
|
|
fprintf (stderr, "Bad stab: %s\n", p);
|
|
}
|
|
|
|
/* Warn about something in a stab string. */
|
|
|
|
static void
|
|
warn_stab (p, err)
|
|
const char *p;
|
|
const char *err;
|
|
{
|
|
fprintf (stderr, "Warning: %s: %s\n", err, p);
|
|
}
|
|
|
|
/* Create a handle to parse stabs symbols with. */
|
|
|
|
/*ARGSUSED*/
|
|
PTR
|
|
start_stab (dhandle)
|
|
PTR dhandle;
|
|
{
|
|
struct stab_handle *ret;
|
|
|
|
ret = (struct stab_handle *) xmalloc (sizeof *ret);
|
|
memset (ret, 0, sizeof *ret);
|
|
ret->files = 1;
|
|
ret->file_types = (struct stab_types **) xmalloc (sizeof *ret->file_types);
|
|
ret->file_types[0] = NULL;
|
|
return (PTR) ret;
|
|
}
|
|
|
|
/* When we have processed all the stabs information, we need to go
|
|
through and fill in all the undefined tags. */
|
|
|
|
boolean
|
|
finish_stab (dhandle, handle)
|
|
PTR dhandle;
|
|
PTR handle;
|
|
{
|
|
struct stab_handle *info = (struct stab_handle *) handle;
|
|
struct stab_tag *st;
|
|
|
|
if (info->within_function)
|
|
{
|
|
if (! debug_end_function (dhandle, (bfd_vma) -1))
|
|
return false;
|
|
info->within_function = false;
|
|
}
|
|
|
|
for (st = info->tags; st != NULL; st = st->next)
|
|
{
|
|
st->slot = debug_make_undefined_tagged_type (dhandle, st->name,
|
|
st->kind);
|
|
if (st->slot == DEBUG_TYPE_NULL)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Handle a single stabs symbol. */
|
|
|
|
boolean
|
|
parse_stab (dhandle, handle, type, desc, value, string)
|
|
PTR dhandle;
|
|
PTR handle;
|
|
int type;
|
|
int desc;
|
|
bfd_vma value;
|
|
const char *string;
|
|
{
|
|
struct stab_handle *info = (struct stab_handle *) handle;
|
|
|
|
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->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->function_start_offset))
|
|
return false;
|
|
|
|
--info->block_depth;
|
|
if (info->block_depth == 0)
|
|
{
|
|
info->within_function = false;
|
|
if (! debug_end_function (dhandle,
|
|
value + info->function_start_offset))
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case N_SO:
|
|
/* Start a file. If we get two in a row, the first is the
|
|
directory name. An empty string is emitted by gcc at the end
|
|
of a compilation unit. */
|
|
if (*string == '\0')
|
|
{
|
|
if (info->within_function)
|
|
{
|
|
if (! debug_end_function (dhandle, (bfd_vma) -1))
|
|
return false;
|
|
info->within_function = false;
|
|
}
|
|
return true;
|
|
}
|
|
info->gcc_compiled = 0;
|
|
info->n_opt_found = false;
|
|
if (info->last_type == N_SO)
|
|
{
|
|
char *o;
|
|
|
|
if (! debug_append_filename (dhandle, string))
|
|
return false;
|
|
o = info->main_filename;
|
|
info->main_filename = concat (o, string, (const char *) NULL);
|
|
free (o);
|
|
}
|
|
else
|
|
{
|
|
if (! debug_set_filename (dhandle, string))
|
|
return false;
|
|
if (info->main_filename != NULL)
|
|
free (info->main_filename);
|
|
info->main_filename = xstrdup (string);
|
|
|
|
/* 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;
|
|
}
|
|
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);
|
|
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. */
|
|
++info->files;
|
|
info->file_types = ((struct stab_types **)
|
|
xrealloc ((PTR) info->file_types,
|
|
(info->files
|
|
* sizeof *info->file_types)));
|
|
info->file_types[info->files - 1] = NULL;
|
|
break;
|
|
|
|
case N_SLINE:
|
|
if (! debug_record_line (dhandle, desc,
|
|
value + info->function_start_offset))
|
|
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;
|
|
|
|
/* 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. */
|
|
default:
|
|
{
|
|
const char *colon;
|
|
|
|
colon = strchr (string, ':');
|
|
if (colon != NULL
|
|
&& (colon[1] == 'f' || colon[1] == 'F'))
|
|
{
|
|
if (info->within_function)
|
|
{
|
|
if (! debug_end_function (dhandle, (bfd_vma) -1))
|
|
return false;
|
|
}
|
|
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:
|
|
break;
|
|
}
|
|
|
|
info->last_type = type;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Parse the stabs string. */
|
|
|
|
static boolean
|
|
parse_stab_string (dhandle, info, stabtype, desc, value, string)
|
|
PTR 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;
|
|
boolean synonym;
|
|
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 ((unsigned char) *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, &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, &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, &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, &p, (debug_type **) NULL)
|
|
== DEBUG_TYPE_NULL)
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'G':
|
|
/* A global symbol. The value must be extracted from the symbol
|
|
table. */
|
|
dtype = parse_stab_type (dhandle, info, &p, (debug_type **) NULL);
|
|
if (dtype == DEBUG_TYPE_NULL)
|
|
return false;
|
|
if (! stab_record_variable (dhandle, info, name, dtype, DEBUG_GLOBAL,
|
|
(bfd_vma) -1))
|
|
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, &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, &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, &p, (debug_type **) NULL);
|
|
if (dtype != DEBUG_TYPE_NULL)
|
|
dtype = debug_make_pointer_type (dhandle,
|
|
debug_make_function_type (dhandle,
|
|
dtype));
|
|
}
|
|
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, &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, &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, &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, &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, &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, &p, &slot);
|
|
if (dtype == DEBUG_TYPE_NULL)
|
|
return false;
|
|
if (name == NULL)
|
|
return true;
|
|
|
|
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. */
|
|
{
|
|
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, &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, &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, &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, &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. */
|
|
|
|
static debug_type
|
|
parse_stab_type (dhandle, info, pp, slotp)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
debug_type **slotp;
|
|
{
|
|
const char *orig;
|
|
int typenums[2];
|
|
int size;
|
|
boolean stringp;
|
|
int descriptor;
|
|
debug_type dtype;
|
|
|
|
if (slotp != NULL)
|
|
*slotp = NULL;
|
|
|
|
orig = *pp;
|
|
|
|
size = -1;
|
|
stringp = 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 ((unsigned char) **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 ((unsigned char) *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);
|
|
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;
|
|
char *name;
|
|
struct stab_tag *st;
|
|
|
|
/* 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;
|
|
}
|
|
while (q1 != NULL && p > q1 && p[1] == ':')
|
|
{
|
|
q2 = strchr (q1, '>');
|
|
if (q2 == NULL || q2 < p)
|
|
break;
|
|
p += 2;
|
|
p = strchr (p, ':');
|
|
if (p == NULL)
|
|
{
|
|
bad_stab (orig);
|
|
return DEBUG_TYPE_NULL;
|
|
}
|
|
}
|
|
|
|
name = savestring (*pp, p - *pp);
|
|
|
|
*pp = p + 1;
|
|
|
|
/* We pass DEBUG_KIND_VOID 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_VOID);
|
|
if (dtype != DEBUG_TYPE_NULL)
|
|
{
|
|
free (name);
|
|
if (typenums[0] != -1)
|
|
{
|
|
if (! stab_record_type (dhandle, info, typenums, dtype))
|
|
return DEBUG_TYPE_NULL;
|
|
}
|
|
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)
|
|
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 = code;
|
|
st->slot = DEBUG_TYPE_NULL;
|
|
st->type = debug_make_indirect_type (dhandle, &st->slot, name);
|
|
info->tags = st;
|
|
}
|
|
|
|
dtype = st->type;
|
|
if (typenums[0] != -1)
|
|
{
|
|
if (! stab_record_type (dhandle, info, typenums, dtype))
|
|
return DEBUG_TYPE_NULL;
|
|
}
|
|
return dtype;
|
|
}
|
|
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, 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, pp,
|
|
(debug_type **) NULL));
|
|
break;
|
|
|
|
case '&':
|
|
/* Reference to another type. */
|
|
dtype = (debug_make_reference_type
|
|
(dhandle,
|
|
parse_stab_type (dhandle, info, 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, pp, (debug_type **) NULL)));
|
|
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, 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, 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, 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, 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, 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, NULL);
|
|
}
|
|
else
|
|
{
|
|
debug_type domain;
|
|
debug_type return_type;
|
|
debug_type *args;
|
|
unsigned int n;
|
|
unsigned int alloc;
|
|
|
|
domain = parse_stab_type (dhandle, info, 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, 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 ((PTR) args, alloc * sizeof *args));
|
|
}
|
|
|
|
args[n] = parse_stab_type (dhandle, info, pp,
|
|
(debug_type **) NULL);
|
|
if (args[n] == DEBUG_TYPE_NULL)
|
|
return DEBUG_TYPE_NULL;
|
|
++n;
|
|
}
|
|
++*pp;
|
|
|
|
args[n] = DEBUG_TYPE_NULL;
|
|
|
|
dtype = debug_make_method_type (dhandle, return_type, domain, args);
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
/* Range type. */
|
|
dtype = parse_stab_range_type (dhandle, info, 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, 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, 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 false;
|
|
}
|
|
|
|
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 boolean
|
|
parse_stab_type_number (pp, typenums)
|
|
const char **pp;
|
|
int *typenums;
|
|
{
|
|
const char *orig;
|
|
|
|
orig = *pp;
|
|
|
|
if (**pp != '(')
|
|
{
|
|
typenums[0] = 0;
|
|
typenums[1] = (int) parse_number (pp, (boolean *) NULL);
|
|
}
|
|
else
|
|
{
|
|
++*pp;
|
|
typenums[0] = (int) parse_number (pp, (boolean *) NULL);
|
|
if (**pp != ',')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
typenums[1] = (int) parse_number (pp, (boolean *) NULL);
|
|
if (**pp != ')')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Parse a range type. */
|
|
|
|
static debug_type
|
|
parse_stab_range_type (dhandle, info, pp, typenums)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
const int *typenums;
|
|
{
|
|
const char *orig;
|
|
int rangenums[2];
|
|
boolean self_subrange;
|
|
debug_type index_type;
|
|
const char *s2, *s3;
|
|
bfd_signed_vma n2, n3;
|
|
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, 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);
|
|
|
|
/* 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)
|
|
{
|
|
/* 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);
|
|
/* -1 is used for the upper bound of (4 byte) "unsigned int"
|
|
and "unsigned long", and we already checked for that, so
|
|
don't need to test for it here. */
|
|
}
|
|
else if (n3 == 0
|
|
&& n2 < 0
|
|
&& (self_subrange || n2 == -8))
|
|
return debug_make_int_type (dhandle, - n2, true);
|
|
else if (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);
|
|
}
|
|
}
|
|
|
|
/* 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 (dhandle, pp)
|
|
PTR dhandle;
|
|
const char **pp;
|
|
{
|
|
const char *orig;
|
|
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, (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, (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, (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 (dhandle, pp)
|
|
PTR 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, (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, (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 (dhandle, pp)
|
|
PTR 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, (boolean *) NULL);
|
|
if (**pp != ',')
|
|
{
|
|
bad_stab (orig);
|
|
return DEBUG_TYPE_NULL;
|
|
}
|
|
++*pp;
|
|
|
|
if (n + 1 >= alloc)
|
|
{
|
|
alloc += 10;
|
|
names = ((const char **)
|
|
xrealloc ((PTR) names, alloc * sizeof *names));
|
|
values = ((bfd_signed_vma *)
|
|
xrealloc ((PTR) 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 (dhandle, info, pp, structp, typenums)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
boolean structp;
|
|
const int *typenums;
|
|
{
|
|
const char *orig;
|
|
bfd_vma size;
|
|
debug_baseclass *baseclasses;
|
|
debug_field *fields;
|
|
boolean statics;
|
|
debug_method *methods;
|
|
debug_type vptrbase;
|
|
boolean ownvptr;
|
|
|
|
orig = *pp;
|
|
|
|
/* Get the size. */
|
|
size = parse_number (pp, (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, pp, &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 boolean
|
|
parse_stab_baseclasses (dhandle, info, pp, retp)
|
|
PTR 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, (boolean *) NULL);
|
|
|
|
if (**pp != ',')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
|
|
classes = (debug_baseclass *) xmalloc ((c + 1) * sizeof (**retp));
|
|
|
|
for (i = 0; i < c; i++)
|
|
{
|
|
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, (boolean *) NULL);
|
|
if (**pp != ',')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
|
|
type = parse_stab_type (dhandle, info, 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 boolean
|
|
parse_stab_struct_fields (dhandle, info, pp, retp, staticsp)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
debug_field **retp;
|
|
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 ((PTR) 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 boolean
|
|
parse_stab_cpp_abbrev (dhandle, info, pp, retp)
|
|
PTR 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, 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, pp, (debug_type **) NULL);
|
|
if (**pp != ',')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
|
|
bitpos = parse_number (pp, (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 boolean
|
|
parse_stab_one_struct_field (dhandle, info, pp, p, retp, staticsp)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
const char *p;
|
|
debug_field *retp;
|
|
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, 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, (boolean *) NULL);
|
|
if (**pp != ',')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
|
|
bitsize = parse_number (pp, (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 boolean
|
|
parse_stab_members (dhandle, info, pp, retp)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
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;
|
|
char *argtypes;
|
|
enum debug_visibility visibility;
|
|
boolean constp, volatilep, staticp;
|
|
bfd_vma voffset;
|
|
debug_type context;
|
|
|
|
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, 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;
|
|
}
|
|
|
|
/* FIXME: gdb sets is_stub here. */
|
|
|
|
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 set to distinguish pointers-to-methods from
|
|
virtual function indicies. Since the array is in
|
|
words, the quantity must be shifted left by 1 on 16
|
|
bit machine, and by 2 on 32 bit machine, forcing the
|
|
sign bit out, and usable as a valid index into the
|
|
array. Remove the sign bit here. */
|
|
++*pp;
|
|
voffset = parse_number (pp, (boolean *) NULL);
|
|
if (**pp != ';')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
voffset &= 0x7fffffff;
|
|
voffset += 2;
|
|
|
|
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, pp,
|
|
(debug_type **) NULL);
|
|
if (**pp == ':')
|
|
{
|
|
/* g++ version 1 overloaded methods. */
|
|
}
|
|
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;
|
|
/* FIXME: gdb sets is_stub here. */
|
|
context = DEBUG_TYPE_NULL;
|
|
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 (cvars + 1 >= allocvars)
|
|
{
|
|
allocvars += 10;
|
|
variants = ((debug_method_variant *)
|
|
xrealloc ((PTR) variants,
|
|
allocvars * sizeof *variants));
|
|
}
|
|
|
|
if (! staticp)
|
|
variants[cvars] = debug_make_method_variant (dhandle, argtypes,
|
|
type, visibility,
|
|
constp, volatilep,
|
|
voffset, context);
|
|
else
|
|
variants[cvars] = debug_make_static_method_variant (dhandle,
|
|
argtypes,
|
|
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 ((PTR) 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;
|
|
}
|
|
|
|
/* 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 boolean
|
|
parse_stab_tilde_field (dhandle, info, pp, typenums, retvptrbase, retownvptr)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
const int *typenums;
|
|
debug_type *retvptrbase;
|
|
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, 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 (dhandle, info, pp, stringp)
|
|
PTR dhandle;
|
|
struct stab_handle *info;
|
|
const char **pp;
|
|
boolean stringp;
|
|
{
|
|
const char *orig;
|
|
debug_type index_type;
|
|
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. */
|
|
|
|
index_type = parse_stab_type (dhandle, info, pp, (debug_type **) NULL);
|
|
if (**pp != ';')
|
|
{
|
|
bad_stab (orig);
|
|
return DEBUG_TYPE_NULL;
|
|
}
|
|
++*pp;
|
|
|
|
adjustable = false;
|
|
|
|
if (! isdigit ((unsigned char) **pp) && **pp != '-')
|
|
{
|
|
++*pp;
|
|
adjustable = true;
|
|
}
|
|
|
|
lower = (bfd_signed_vma) parse_number (pp, (boolean *) NULL);
|
|
if (**pp != ';')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
|
|
if (! isdigit ((unsigned char) **pp) && **pp != '-')
|
|
{
|
|
++*pp;
|
|
adjustable = true;
|
|
}
|
|
|
|
upper = (bfd_signed_vma) parse_number (pp, (boolean *) NULL);
|
|
if (**pp != ';')
|
|
{
|
|
bad_stab (orig);
|
|
return false;
|
|
}
|
|
++*pp;
|
|
|
|
element_type = parse_stab_type (dhandle, info, pp, (debug_type **) NULL);
|
|
if (element_type == DEBUG_TYPE_NULL)
|
|
return false;
|
|
|
|
if (adjustable)
|
|
{
|
|
lower = 0;
|
|
upper = -1;
|
|
}
|
|
|
|
return debug_make_array_type (dhandle, element_type, index_type, lower,
|
|
upper, stringp);
|
|
}
|
|
|
|
/* Keep a stack of N_BINCL include files. */
|
|
|
|
struct bincl_file
|
|
{
|
|
struct bincl_file *next;
|
|
const char *name;
|
|
};
|
|
|
|
/* Start a new N_BINCL file, pushing it onto the stack. */
|
|
|
|
static void
|
|
push_bincl (info, name)
|
|
struct stab_handle *info;
|
|
const char *name;
|
|
{
|
|
struct bincl_file *n;
|
|
|
|
n = (struct bincl_file *) xmalloc (sizeof *n);
|
|
n->next = info->bincl_stack;
|
|
n->name = name;
|
|
info->bincl_stack = n;
|
|
|
|
++info->files;
|
|
info->file_types = ((struct stab_types **)
|
|
xrealloc ((PTR) info->file_types,
|
|
(info->files
|
|
* sizeof *info->file_types)));
|
|
info->file_types[info->files - 1] = NULL;
|
|
}
|
|
|
|
/* Finish an N_BINCL file, at an N_EINCL, popping the name off the
|
|
stack. */
|
|
|
|
static const char *
|
|
pop_bincl (info)
|
|
struct stab_handle *info;
|
|
{
|
|
struct bincl_file *o;
|
|
|
|
o = info->bincl_stack;
|
|
if (o == NULL)
|
|
return info->main_filename;
|
|
info->bincl_stack = o->next;
|
|
free (o);
|
|
if (info->bincl_stack == NULL)
|
|
return info->main_filename;
|
|
return info->bincl_stack->name;
|
|
}
|
|
|
|
/* 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 boolean
|
|
stab_record_variable (dhandle, info, name, type, kind, val)
|
|
PTR 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 (! 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 boolean
|
|
stab_emit_pending_vars (dhandle, info)
|
|
PTR 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 (info, typenums)
|
|
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 (dhandle, info, typenums)
|
|
PTR 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 boolean
|
|
stab_record_type (dhandle, info, typenums, type)
|
|
PTR dhandle;
|
|
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 (dhandle, info, typenum)
|
|
PTR 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;
|
|
}
|