Implement generic debugging support. Implement a stabs reader and
authorIan Lance Taylor <ian@airs.com>
Tue, 2 Jan 1996 22:48:58 +0000 (22:48 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 2 Jan 1996 22:48:58 +0000 (22:48 +0000)
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.

binutils/.Sanitize
binutils/binutils.texi
binutils/budbg.h [new file with mode: 0644]
binutils/debug.c [new file with mode: 0644]
binutils/debug.h [new file with mode: 0644]
binutils/objdump.1
binutils/objdump.c
binutils/prdbg.c [new file with mode: 0644]
binutils/rddbg.c [new file with mode: 0644]
binutils/stabs.c [new file with mode: 0644]

index a3fb42254ba01bb8931ba6c82a2183d487f7859d..be71ab7a94fc0e7d5be3af7091d0d0368f39efff 100644 (file)
@@ -40,6 +40,7 @@ arsup.h
 binutils.texi
 bucomm.c
 bucomm.h
+budbg.h
 coffdump.c
 coffgrok.c
 coffgrok.h
@@ -49,6 +50,9 @@ configure.bat
 configure.in
 cxxfilt.man
 dlltool.c
+debug.c
+debug.h
+dep-in.sed
 defparse.y
 deflex.l
 filemode.c
@@ -72,12 +76,15 @@ objcopy.1
 objcopy.c
 objdump.1
 objdump.c
+prdbg.c
 ranlib.1
 ranlib.sh
+rddbg.c
 sanity.sh
 size.1
 size.c
 srconv.c
+stabs.c
 strings.1
 strings.c
 strip.1
index d53dbb12ba5d700b5849fe71e4989532b9ecbdf2..fbec8beace86ebc16eb9844c8212005586a10ba8 100644 (file)
@@ -955,7 +955,7 @@ Show a summary of the options to @code{objcopy}.
 
 @smallexample
 objdump [ -a | --archive-headers ] 
-        [ -b @var{bfdname} | --target=@var{bfdname} ]
+        [ -b @var{bfdname} | --target=@var{bfdname} ] [ --debugging ]
         [ -d | --disassemble ]  [ -D | --disassemble-all ] 
         [ -f | --file-headers ]
         [ -h | --section-headers | --headers ]  [ -i | --info ]
@@ -1010,6 +1010,11 @@ file in the format produced by Oasys compilers.  You can list the
 formats available with the @samp{-i} option.
 @xref{Target Selection}, for more information.
 
+@item --debugging
+Display debugging information.  This attempts to parse debugging
+information stored in the file and print it out using a C like syntax.
+Only certain types of debugging information have been implemented.
+
 @item -d
 @itemx --disassemble
 @cindex disassembling object code
diff --git a/binutils/budbg.h b/binutils/budbg.h
new file mode 100644 (file)
index 0000000..e63841e
--- /dev/null
@@ -0,0 +1,43 @@
+/* budbg.c -- Interfaces to the generic debugging information routines.
+   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.  */
+
+#ifndef BUDBG_H
+#define BUDBG_H
+
+#include <stdio.h>
+
+/* Routine used to read generic debugging information.  */
+
+extern PTR read_debugging_info PARAMS ((bfd *));
+
+/* Routine used to print generic debugging information.  */
+
+extern boolean print_debugging_info PARAMS ((FILE *, PTR));
+
+/* Routines used to read stabs information.  */
+
+extern PTR start_stab PARAMS ((PTR));
+
+extern boolean finish_stab PARAMS ((PTR, PTR));
+
+extern boolean parse_stab PARAMS ((PTR, PTR, int, int, bfd_vma, const char *));
+
+#endif
diff --git a/binutils/debug.c b/binutils/debug.c
new file mode 100644 (file)
index 0000000..18eb529
--- /dev/null
@@ -0,0 +1,2572 @@
+/* debug.c -- Handle generic 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 implements a generic debugging format.  We may eventually
+   have readers which convert different formats into this generic
+   format, and writers which write it out.  The initial impetus for
+   this was writing a convertor from stabs to HP IEEE-695 debugging
+   format.  */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "bfd.h"
+#include "bucomm.h"
+#include "libiberty.h"
+#include "debug.h"
+
+/* Global information we keep for debugging.  A pointer to this
+   structure is the debugging handle passed to all the routines.  */
+
+struct debug_handle
+{
+  /* A linked list of compilation units.  */
+  struct debug_unit *units;
+  /* The current compilation unit.  */
+  struct debug_unit *current_unit;
+  /* The current source file.  */
+  struct debug_file *current_file;
+  /* The current function.  */
+  struct debug_function *current_function;
+  /* The current block.  */
+  struct debug_block *current_block;
+  /* The current line number information for the current block.  */
+  struct debug_lineno *current_lineno;
+  /* Mark.  This is used by debug_write.  */
+  unsigned int mark;
+  /* Another mark used by debug_write.  */
+  unsigned int class_mark;
+};
+
+/* Information we keep for a single compilation unit.  */
+
+struct debug_unit
+{
+  /* The next compilation unit.  */
+  struct debug_unit *next;
+  /* A list of files included in this compilation unit.  The first
+     file is always the main one, and that is where the main file name
+     is stored.  */
+  struct debug_file *files;
+};
+
+/* Information kept for a single source file.  */
+
+struct debug_file
+{
+  /* The next source file in this compilation unit.  */
+  struct debug_file *next;
+  /* The name of the source file.  */
+  const char *filename;
+  /* Global functions, variables, types, etc.  */
+  struct debug_namespace *globals;
+};
+
+/* A type.  */
+
+struct debug_type
+{
+  /* Kind of type.  */
+  enum debug_type_kind kind;
+  /* Size of type (0 if not known).  */
+  unsigned int size;
+  /* Type which is a pointer to this type.  */
+  debug_type pointer;
+  /* Tagged union with additional information about the type.  */
+  union
+    {
+      /* DEBUG_KIND_INDIRECT.  */
+      struct debug_indirect_type *kindirect;
+      /* DEBUG_KIND_INT.  */
+      /* Whether the integer is unsigned.  */
+      boolean kint;
+      /* DEBUG_KIND_STRUCT, DEBUG_KIND_UNION, DEBUG_KIND_CLASS,
+         DEBUG_KIND_UNION_CLASS.  */
+      struct debug_class_type *kclass;
+      /* DEBUG_KIND_ENUM.  */
+      struct debug_enum_type *kenum;
+      /* DEBUG_KIND_POINTER.  */
+      struct debug_type *kpointer;
+      /* DEBUG_KIND_FUNCTION.  */
+      struct debug_function_type *kfunction;
+      /* DEBUG_KIND_REFERENCE.  */
+      struct debug_type *kreference;
+      /* DEBUG_KIND_RANGE.  */
+      struct debug_range_type *krange;
+      /* DEBUG_KIND_ARRAY.  */
+      struct debug_array_type *karray;
+      /* DEBUG_KIND_SET.  */
+      struct debug_set_type *kset;
+      /* DEBUG_KIND_OFFSET.  */
+      struct debug_offset_type *koffset;
+      /* DEBUG_KIND_METHOD.  */
+      struct debug_method_type *kmethod;
+      /* DEBUG_KIND_CONST.  */
+      struct debug_type *kconst;
+      /* DEBUG_KIND_VOLATILE.  */
+      struct debug_type *kvolatile;
+      /* DEBUG_KIND_NAMED, DEBUG_KIND_TAGGED.  */
+      struct debug_named_type *knamed;
+    } u;
+};
+
+/* Information kept for an indirect type.  */
+
+struct debug_indirect_type
+{
+  /* Slot where the final type will appear.  */
+  debug_type *slot;
+  /* Tag.  */
+  const char *tag;
+};
+
+/* Information kept for a struct, union, or class.  */
+
+struct debug_class_type
+{
+  /* NULL terminated array of fields.  */
+  debug_field *fields;
+  /* A mark field used to avoid recursively printing out structs.  */
+  unsigned int mark;
+  /* The remaining fields are only used for DEBUG_KIND_CLASS and
+     DEBUG_KIND_UNION_CLASS.  */
+  /* NULL terminated array of base classes.  */
+  debug_baseclass *baseclasses;
+  /* NULL terminated array of methods.  */
+  debug_method *methods;
+  /* The type of the class providing the virtual function table for
+     this class.  This may point to the type itself.  */
+  debug_type vptrbase;
+};
+
+/* Information kept for an enum.  */
+
+struct debug_enum_type
+{
+  /* NULL terminated array of names.  */
+  const char **names;
+  /* Array of corresponding values.  */
+  bfd_signed_vma *values;
+};
+
+/* Information kept for a function.  FIXME: We should be able to
+   record the parameter types.  */
+
+struct debug_function_type
+{
+  /* Return type.  */
+  debug_type return_type;
+};
+
+/* Information kept for a range.  */
+
+struct debug_range_type
+{
+  /* Range base type.  */
+  debug_type type;
+  /* Lower bound.  */
+  bfd_signed_vma lower;
+  /* Upper bound.  */
+  bfd_signed_vma upper;
+};
+
+/* Information kept for an array.  */
+
+struct debug_array_type
+{
+  /* Element type.  */
+  debug_type element_type;
+  /* Range type.  */
+  debug_type range_type;
+  /* Lower bound.  */
+  bfd_signed_vma lower;
+  /* Upper bound.  */
+  bfd_signed_vma upper;
+  /* Whether this array is really a string.  */
+  boolean stringp;
+};
+
+/* Information kept for a set.  */
+
+struct debug_set_type
+{
+  /* Base type.  */
+  debug_type type;
+  /* Whether this set is really a bitstring.  */
+  boolean bitstringp;
+};
+
+/* Information kept for an offset type (a based pointer).  */
+
+struct debug_offset_type
+{
+  /* The type the pointer is an offset from.  */
+  debug_type base_type;
+  /* The type the pointer points to.  */
+  debug_type target_type;
+};
+
+/* Information kept for a method type.  */
+
+struct debug_method_type
+{
+  /* The return type.  */
+  debug_type return_type;
+  /* The object type which this method is for.  */
+  debug_type domain_type;
+  /* A NULL terminated array of argument types.  */
+  debug_type *arg_types;
+};
+
+/* Information kept for a named type.  */
+
+struct debug_named_type
+{
+  /* Name.  */
+  struct debug_name *name;
+  /* Real type.  */
+  debug_type type;
+};
+
+/* A field in a struct or union.  */
+
+struct debug_field
+{
+  /* Name of the field.  */
+  const char *name;
+  /* Type of the field.  */
+  struct debug_type *type;
+  /* Visibility of the field.  */
+  enum debug_visibility visibility;
+  /* Whether this is a static member.  */
+  boolean static_member;
+  union
+    {
+      /* If static_member is false.  */
+      struct
+       {
+         /* Bit position of the field in the struct.  */
+         unsigned int bitpos;
+         /* Size of the field in bits.  */
+         unsigned int bitsize;
+       } f;
+      /* If static_member is true.  */
+      struct
+       {
+         const char *physname;
+       } s;
+    } u;
+};
+
+/* A base class for an object.  */
+
+struct debug_baseclass
+{
+  /* Type of the base class.  */
+  struct debug_type *type;
+  /* Bit position of the base class in the object.  */
+  unsigned int bitpos;
+  /* Whether the base class is virtual.  */
+  boolean virtual;
+  /* Visibility of the base class.  */
+  enum debug_visibility visibility;
+};
+
+/* A method of an object.  */
+
+struct debug_method
+{
+  /* The name of the method.  */
+  const char *name;
+  /* A NULL terminated array of different types of variants.  */
+  struct debug_method_variant **variants;
+};
+
+/* The variants of a method function of an object.  These indicate
+   which method to run.  */
+
+struct debug_method_variant
+{
+  /* The argument types of the function.  */
+  const char *argtypes;
+  /* The type of the function.  */
+  struct debug_type *type;
+  /* The visibility of the function.  */
+  enum debug_visibility visibility;
+  /* Whether the function is const.  */
+  boolean constp;
+  /* Whether the function is volatile.  */
+  boolean volatilep;
+  /* The offset to the function in the virtual function table.  */
+  bfd_vma voffset;
+  /* If voffset is VOFFSET_STATIC_METHOD, this is a static method.  */
+#define VOFFSET_STATIC_METHOD (1)
+  /* Context of a virtual method function.  */
+  struct debug_type *context;
+};
+
+/* A variable.  This is the information we keep for a variable object.
+   This has no name; a name is associated with a variable in a
+   debug_name structure.  */
+
+struct debug_variable
+{
+  /* Kind of variable.  */
+  enum debug_var_kind kind;
+  /* Type.  */
+  debug_type type;
+  /* Value.  The interpretation of the value depends upon kind.  */
+  bfd_vma val;
+};
+
+/* A function.  This has no name; a name is associated with a function
+   in a debug_name structure.  */
+
+struct debug_function
+{
+  /* Return type.  */
+  debug_type return_type;
+  /* Parameter information.  */
+  struct debug_parameter *parameters;
+  /* Block information.  The first structure on the list is the main
+     block of the function, and describes function local variables.  */
+  struct debug_block *blocks;
+};
+
+/* A function parameter.  */
+
+struct debug_parameter
+{
+  /* Next parameter.  */
+  struct debug_parameter *next;
+  /* Name.  */
+  const char *name;
+  /* Type.  */
+  debug_type type;
+  /* Kind.  */
+  enum debug_parm_kind kind;
+  /* Value (meaning depends upon kind).  */
+  bfd_vma val;
+};
+
+/* A typed constant.  */
+
+struct debug_typed_constant
+{
+  /* Type.  */
+  debug_type type;
+  /* Value.  FIXME: We may eventually need to support non-integral
+     values.  */
+  bfd_vma val;
+};
+
+/* Information about a block within a function.  */
+
+struct debug_block
+{
+  /* Next block with the same parent.  */
+  struct debug_block *next;
+  /* Parent block.  */
+  struct debug_block *parent;
+  /* List of child blocks.  */
+  struct debug_block *children;
+  /* Start address of the block.  */
+  bfd_vma start;
+  /* End address of the block.  */
+  bfd_vma end;
+  /* Local variables.  */
+  struct debug_namespace *locals;
+  /* Line number information.  */
+  struct debug_lineno *linenos;
+};
+
+/* Line number information we keep for a function.  FIXME: This
+   structure is easy to create, but can be very space inefficient.  */
+
+struct debug_lineno
+{
+  /* More line number information for this block.  */
+  struct debug_lineno *next;
+  /* Source file.  */
+  struct debug_file *file;
+  /* Line numbers, terminated by a -1 or the end of the array.  */
+#define DEBUG_LINENO_COUNT 10
+  unsigned long linenos[DEBUG_LINENO_COUNT];
+  /* Addresses for the line numbers.  */
+  bfd_vma addrs[DEBUG_LINENO_COUNT];
+};
+
+/* A namespace.  This is a mapping from names to objects.  FIXME: This
+   should be implemented as a hash table.  */
+
+struct debug_namespace
+{
+  /* List of items in this namespace.  */
+  struct debug_name *list;
+  /* Pointer to where the next item in this namespace should go.  */
+  struct debug_name **tail;
+};
+
+/* Kinds of objects that appear in a namespace.  */
+
+enum debug_object_kind
+{
+  /* A type.  */
+  DEBUG_OBJECT_TYPE,
+  /* A tagged type (really a different sort of namespace).  */
+  DEBUG_OBJECT_TAG,
+  /* A variable.  */
+  DEBUG_OBJECT_VARIABLE,
+  /* A function.  */
+  DEBUG_OBJECT_FUNCTION,
+  /* An integer constant.  */
+  DEBUG_OBJECT_INT_CONSTANT,
+  /* A floating point constant.  */
+  DEBUG_OBJECT_FLOAT_CONSTANT,
+  /* A typed constant.  */
+  DEBUG_OBJECT_TYPED_CONSTANT
+};
+
+/* Linkage of an object that appears in a namespace.  */
+
+enum debug_object_linkage
+{
+  /* Local variable.  */
+  DEBUG_LINKAGE_AUTOMATIC,
+  /* Static--either file static or function static, depending upon the
+     namespace is.  */
+  DEBUG_LINKAGE_STATIC,
+  /* Global.  */
+  DEBUG_LINKAGE_GLOBAL,
+  /* No linkage.  */
+  DEBUG_LINKAGE_NONE
+};
+
+/* A name in a namespace.  */
+
+struct debug_name
+{
+  /* Next name in this namespace.  */
+  struct debug_name *next;
+  /* Name.  */
+  const char *name;
+  /* Mark.  This is used by debug_write.  */
+  unsigned int mark;
+  /* Kind of object.  */
+  enum debug_object_kind kind;
+  /* Linkage of object.  */
+  enum debug_object_linkage linkage;
+  /* Tagged union with additional information about the object.  */
+  union
+    {
+      /* DEBUG_OBJECT_TYPE.  */
+      struct debug_type *type;
+      /* DEBUG_OBJECT_TAG.  */
+      struct debug_type *tag;
+      /* DEBUG_OBJECT_VARIABLE.  */
+      struct debug_variable *variable;
+      /* DEBUG_OBJECT_FUNCTION.  */
+      struct debug_function *function;
+      /* DEBUG_OBJECT_INT_CONSTANT.  */
+      bfd_vma int_constant;
+      /* DEBUG_OBJECT_FLOAT_CONSTANT.  */
+      double float_constant;
+      /* DEBUG_OBJECT_TYPED_CONSTANT.  */
+      struct debug_typed_constant *typed_constant;
+    } u;
+};
+
+static void debug_error PARAMS ((const char *));
+static struct debug_name *debug_add_to_namespace
+  PARAMS ((struct debug_handle *, struct debug_namespace **, const char *,
+          enum debug_object_kind, enum debug_object_linkage));
+static struct debug_name *debug_add_to_current_namespace
+  PARAMS ((struct debug_handle *, const char *, enum debug_object_kind,
+          enum debug_object_linkage));
+static struct debug_type *debug_make_type
+  PARAMS ((struct debug_handle *, enum debug_type_kind, unsigned int));
+static boolean debug_write_name
+  PARAMS ((struct debug_handle *, const struct debug_write_fns *, PTR,
+          struct debug_name *));
+static boolean debug_write_type
+  PARAMS ((struct debug_handle *, const struct debug_write_fns *, PTR,
+          struct debug_type *, struct debug_name *));
+static boolean debug_write_class_type
+  PARAMS ((struct debug_handle *, const struct debug_write_fns *, PTR,
+          struct debug_type *));
+static boolean debug_write_function
+  PARAMS ((struct debug_handle *, const struct debug_write_fns *, PTR,
+          const char *, enum debug_object_linkage, struct debug_function *));
+static boolean debug_write_block
+  PARAMS ((struct debug_handle *, const struct debug_write_fns *, PTR,
+          struct debug_block *));
+\f
+/* Issue an error message.  */
+
+static void
+debug_error (message)
+     const char *message;
+{
+  fprintf (stderr, "%s\n", message);
+}
+
+/* Add an object to a namespace.  */
+
+static struct debug_name *
+debug_add_to_namespace (info, nsp, name, kind, linkage)
+     struct debug_handle *info;
+     struct debug_namespace **nsp;
+     const char *name;
+     enum debug_object_kind kind;
+     enum debug_object_linkage linkage;
+{
+  struct debug_name *n;
+  struct debug_namespace *ns;
+
+  n = (struct debug_name *) xmalloc (sizeof *n);
+  memset (n, 0, sizeof *n);
+
+  n->name = name;
+  n->kind = kind;
+  n->linkage = linkage;
+
+  ns = *nsp;
+  if (ns == NULL)
+    {
+      ns = (struct debug_namespace *) xmalloc (sizeof *ns);
+      memset (ns, 0, sizeof *ns);
+
+      ns->tail = &ns->list;
+
+      *nsp = ns;
+    }
+
+  *ns->tail = n;
+  ns->tail = &n->next;
+
+  return n;
+}
+
+/* Add an object to the current namespace.  */
+
+static struct debug_name *
+debug_add_to_current_namespace (info, name, kind, linkage)
+     struct debug_handle *info;
+     const char *name;
+     enum debug_object_kind kind;
+     enum debug_object_linkage linkage;
+{
+  struct debug_namespace **nsp;
+
+  if (info->current_unit == NULL
+      || info->current_file == NULL)
+    {
+      debug_error ("debug_add_to_current_namespace: no current file");
+      return NULL;
+    }
+
+  if (info->current_block != NULL)
+    nsp = &info->current_block->locals;
+  else
+    nsp = &info->current_file->globals;
+
+  return debug_add_to_namespace (info, nsp, name, kind, linkage);
+}
+\f
+/* Return a handle for debugging information.  */
+
+PTR
+debug_init ()
+{
+  struct debug_handle *ret;
+
+  ret = (struct debug_handle *) xmalloc (sizeof *ret);
+  memset (ret, 0, sizeof *ret);
+  return (PTR) ret;
+}
+
+/* Set the source filename.  This implicitly starts a new compilation
+   unit.  */
+
+boolean
+debug_set_filename (handle, name)
+     PTR handle;
+     const char *name;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_file *nfile;
+  struct debug_unit *nunit;
+
+  if (name == NULL)
+    name = "";
+
+  nfile = (struct debug_file *) xmalloc (sizeof *nfile);
+  memset (nfile, 0, sizeof *nfile);
+
+  nfile->filename = name;
+
+  nunit = (struct debug_unit *) xmalloc (sizeof *nunit);
+  memset (nunit, 0, sizeof *nunit);
+
+  nunit->files = nfile;
+  info->current_file = nfile;
+
+  if (info->current_unit != NULL)
+    info->current_unit->next = nunit;
+  else
+    {
+      assert (info->units == NULL);
+      info->units = nunit;
+    }
+
+  info->current_unit = nunit;
+
+  info->current_function = NULL;
+  info->current_block = NULL;
+  info->current_lineno = NULL;
+
+  return true;
+}
+
+/* Append a string to the source filename.  */
+
+boolean
+debug_append_filename (handle, string)
+     PTR handle;
+     const char *string;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  char *n;
+
+  if (string == NULL)
+    string = "";
+
+  if (info->current_unit == NULL)
+    {
+      debug_error ("debug_append_filename: no current file");
+      return false;
+    }
+
+  n = (char *) xmalloc (strlen (info->current_unit->files->filename)
+                       + strlen (string)
+                       + 1);
+  sprintf (n, "%s%s", info->current_unit->files->filename, string);
+  info->current_unit->files->filename = n;
+
+  return true;
+}
+
+/* Change source files to the given file name.  This is used for
+   include files in a single compilation unit.  */
+
+boolean
+debug_start_source (handle, name)
+     PTR handle;
+     const char *name;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_file *f, **pf;
+
+  if (name == NULL)
+    name = "";
+
+  if (info->current_unit == NULL)
+    {
+      debug_error ("debug_start_source: no debug_set_filename call");
+      return false;
+    }
+
+  for (f = info->current_unit->files; f != NULL; f = f->next)
+    {
+      if (f->filename[0] == name[0]
+         && f->filename[1] == name[1]
+         && strcmp (f->filename, name) == 0)
+       {
+         info->current_file = f;
+         return true;
+       }
+    }
+
+  f = (struct debug_file *) xmalloc (sizeof *f);
+  memset (f, 0, sizeof *f);
+
+  f->filename = name;
+
+  for (pf = &info->current_file->next;
+       *pf != NULL;
+       pf = &(*pf)->next)
+    ;
+  *pf = f;
+
+  info->current_file = f;
+
+  return true;
+}
+
+/* Record a function definition.  This implicitly starts a function
+   block.  The debug_type argument is the type of the return value.
+   The boolean indicates whether the function is globally visible.
+   The bfd_vma is the address of the start of the function.  Currently
+   the parameter types are specified by calls to
+   debug_record_parameter.  FIXME: There is no way to specify nested
+   functions.  FIXME: I don't think there is any way to record where a
+   function ends.  */
+
+boolean
+debug_record_function (handle, name, return_type, global, addr)
+     PTR handle;
+     const char *name;
+     debug_type return_type;
+     boolean global;
+     bfd_vma addr;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_function *f;
+  struct debug_block *b;
+  struct debug_name *n;
+
+  if (name == NULL)
+    name = "";
+  if (return_type == NULL)
+    return false;
+
+  if (info->current_unit == NULL)
+    {
+      debug_error ("debug_record_function: no debug_set_filename call");
+      return false;
+    }
+
+  f = (struct debug_function *) xmalloc (sizeof *f);
+  memset (f, 0, sizeof *f);
+
+  f->return_type = return_type;
+
+  b = (struct debug_block *) xmalloc (sizeof *b);
+  memset (b, 0, sizeof *b);
+
+  b->start = addr;
+  b->end = (bfd_vma) -1;
+
+  f->blocks = b;
+
+  info->current_function = f;
+  info->current_block = b;
+  info->current_lineno = NULL;
+
+  /* FIXME: If we could handle nested functions, this would be the
+     place: we would want to use a different namespace.  */
+  n = debug_add_to_namespace (info,
+                             &info->current_file->globals,
+                             name,
+                             DEBUG_OBJECT_FUNCTION,
+                             (global
+                              ? DEBUG_LINKAGE_GLOBAL
+                              : DEBUG_LINKAGE_STATIC));
+  if (n == NULL)
+    return false;
+
+  n->u.function = f;
+
+  return true;
+}
+
+/* Record a parameter for the current function.  */
+
+boolean
+debug_record_parameter (handle, name, type, kind, val)
+     PTR handle;
+     const char *name;
+     debug_type type;
+     enum debug_parm_kind kind;
+     bfd_vma val;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_parameter *p, **pp;
+
+  if (name == NULL || type == NULL)
+    return false;
+
+  if (info->current_unit == NULL
+      || info->current_function == NULL)
+    {
+      debug_error ("debug_record_parameter: no current function");
+      return false;
+    }
+
+  p = (struct debug_parameter *) xmalloc (sizeof *p);
+  memset (p, 0, sizeof *p);
+
+  p->name = name;
+  p->type = type;
+  p->kind = kind;
+  p->val = val;
+
+  for (pp = &info->current_function->parameters;
+       *pp != NULL;
+       pp = &(*pp)->next)
+    ;
+  *pp = p;
+
+  return true;
+}
+
+/* End a function.  FIXME: This should handle function nesting.  */
+
+boolean
+debug_end_function (handle, addr)
+     PTR handle;
+     bfd_vma addr;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+
+  if (info->current_unit == NULL
+      || info->current_block == NULL
+      || info->current_function == NULL)
+    {
+      debug_error ("debug_end_function: no current function");
+      return false;
+    }
+
+  if (info->current_block->parent != NULL)
+    {
+      debug_error ("debug_end_function: some blocks were not closed");
+      return false;
+    }
+
+  info->current_block->end = addr;
+
+  info->current_function = NULL;
+  info->current_block = NULL;
+  info->current_lineno = NULL;
+
+  return true;
+}
+
+/* Start a block in a function.  All local information will be
+   recorded in this block, until the matching call to debug_end_block.
+   debug_start_block and debug_end_block may be nested.  The bfd_vma
+   argument is the address at which this block starts.  */
+
+boolean
+debug_start_block (handle, addr)
+     PTR handle;
+     bfd_vma addr;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_block *b, **pb;
+
+  /* We must always have a current block: debug_record_function sets
+     one up.  */
+  if (info->current_unit == NULL
+      || info->current_block == NULL)
+    {
+      debug_error ("debug_start_block: no current block");
+      return false;
+    }
+
+  b = (struct debug_block *) xmalloc (sizeof *b);
+  memset (b, 0, sizeof *b);
+
+  b->parent = info->current_block;
+  b->start = addr;
+  b->end = (bfd_vma) -1;
+
+  /* This new block is a child of the current block.  */
+  for (pb = &info->current_block->children;
+       *pb != NULL;
+       pb = &(*pb)->next)
+    ;
+  *pb = b;
+
+  info->current_block = b;
+  info->current_lineno = NULL;
+
+  return true;
+}
+
+/* Finish a block in a function.  This matches the call to
+   debug_start_block.  The argument is the address at which this block
+   ends.  */
+
+boolean
+debug_end_block (handle, addr)
+     PTR handle;
+     bfd_vma addr;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_block *parent;
+
+  if (info->current_unit == NULL
+      || info->current_block == NULL)
+    {
+      debug_error ("debug_end_block: no current block");
+      return false;
+    }
+
+  parent = info->current_block->parent;
+  if (parent == NULL)
+    {
+      debug_error ("debug_end_block: attempt to close top level block");
+      return false;
+    }
+
+  info->current_block->end = addr;
+
+  info->current_block = parent;
+  info->current_lineno = NULL;
+
+  return true;
+}
+
+/* Associate a line number in the current source file and function
+   with a given address.  */
+
+boolean
+debug_record_line (handle, lineno, addr)
+     PTR handle;
+     unsigned long lineno;
+     bfd_vma addr;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_lineno *l;
+  unsigned int i;
+
+  if (info->current_unit == NULL
+      || info->current_block == NULL)
+    {
+      debug_error ("debug_record_line: no current block");
+      return false;
+    }
+
+  l = info->current_lineno;
+  if (l != NULL && l->file == info->current_file)
+    {
+      for (i = 0; i < DEBUG_LINENO_COUNT; i++)
+       {
+         if (l->linenos[i] == (unsigned long) -1)
+           {
+             l->linenos[i] = lineno;
+             l->addrs[i] = addr;
+             return true;
+           }
+       }
+    }
+
+  /* If we get here, then either 1) there is no current_lineno
+     structure, which means this is the first line number since we got
+     to this block, 2) the current_lineno structure is for a different
+     file, or 3) the current_lineno structure is full.  Regardless, we
+     want to allocate a new debug_lineno structure, put it in the
+     right place, and make it the new current_lineno structure.  */
+
+  l = (struct debug_lineno *) xmalloc (sizeof *l);
+  memset (l, 0, sizeof *l);
+
+  l->file = info->current_file;
+  l->linenos[0] = lineno;
+  l->addrs[0] = addr;
+  for (i = 1; i < DEBUG_LINENO_COUNT; i++)
+    l->linenos[i] = (unsigned long) -1;
+
+  if (info->current_lineno != NULL)
+    info->current_lineno->next = l;
+  else
+    {
+      struct debug_lineno **pl;
+
+      /* We may be back in this block after dealing with child blocks,
+         which means we may have some line number information for this
+         block even though current_lineno was NULL.  */
+      for (pl = &info->current_block->linenos;
+          *pl != NULL;
+          pl = &(*pl)->next)
+       ;
+      *pl = l;
+    }
+
+  info->current_lineno = l;
+
+  return true;
+}
+
+/* Start a named common block.  This is a block of variables that may
+   move in memory.  */
+
+boolean
+debug_start_common_block (handle, name)
+     PTR handle;
+     const char *name;
+{
+  /* FIXME */
+  debug_error ("debug_start_common_block: not implemented");
+  return false;
+}
+
+/* End a named common block.  */
+
+boolean
+debug_end_common_block (handle, name)
+     PTR handle;
+     const char *name;
+{
+  /* FIXME */
+  debug_error ("debug_end_common_block: not implemented");
+  return false;
+}
+
+/* Record a named integer constant.  */
+
+boolean
+debug_record_int_const (handle, name, val)
+     PTR handle;
+     const char *name;
+     bfd_vma val;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_name *n;
+
+  if (name == NULL)
+    return false;
+
+  n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_INT_CONSTANT,
+                                     DEBUG_LINKAGE_NONE);
+  if (n == NULL)
+    return false;
+
+  n->u.int_constant = val;
+
+  return true;
+}
+
+/* Record a named floating point constant.  */
+
+boolean
+debug_record_float_const (handle, name, val)
+     PTR handle;
+     const char *name;
+     double val;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_name *n;
+
+  if (name == NULL)
+    return false;
+
+  n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_FLOAT_CONSTANT,
+                                     DEBUG_LINKAGE_NONE);
+  if (n == NULL)
+    return false;
+
+  n->u.float_constant = val;
+
+  return true;
+}
+
+/* Record a typed constant with an integral value.  */
+
+boolean
+debug_record_typed_const (handle, name, type, val)
+     PTR handle;
+     const char *name;
+     debug_type type;
+     bfd_vma val;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_name *n;
+  struct debug_typed_constant *tc;
+
+  if (name == NULL || type == NULL)
+    return false;
+
+  n = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_TYPED_CONSTANT,
+                                     DEBUG_LINKAGE_NONE);
+  if (n == NULL)
+    return false;
+
+  tc = (struct debug_typed_constant *) xmalloc (sizeof *tc);
+  memset (tc, 0, sizeof *tc);
+
+  tc->type = type;
+  tc->val = val;
+
+  n->u.typed_constant = tc;
+
+  return true;
+}
+
+/* Record a label.  */
+
+boolean
+debug_record_label (handle, name, type, addr)
+     PTR handle;
+     const char *name;
+     debug_type type;
+     bfd_vma addr;
+{
+  /* FIXME.  */
+  debug_error ("debug_record_label not implemented");
+  return false;
+}
+
+/* Record a variable.  */
+
+boolean
+debug_record_variable (handle, name, type, kind, val)
+     PTR handle;
+     const char *name;
+     debug_type type;
+     enum debug_var_kind kind;
+     bfd_vma val;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_namespace **nsp;
+  enum debug_object_linkage linkage;
+  struct debug_name *n;
+  struct debug_variable *v;
+
+  if (name == NULL || type == NULL)
+    return false;
+
+  if (info->current_unit == NULL
+      || info->current_file == NULL)
+    {
+      debug_error ("debug_record_variable: no current file");
+      return false;
+    }
+
+  if (kind == DEBUG_GLOBAL || kind == DEBUG_STATIC)
+    {
+      nsp = &info->current_file->globals;
+      if (kind == DEBUG_GLOBAL)
+       linkage = DEBUG_LINKAGE_GLOBAL;
+      else
+       linkage = DEBUG_LINKAGE_STATIC;
+    }
+  else
+    {
+      if (info->current_block == NULL)
+       {
+         debug_error ("debug_record_variable: no current block");
+         return false;
+       }
+      nsp = &info->current_block->locals;
+      linkage = DEBUG_LINKAGE_AUTOMATIC;
+    }
+
+  n = debug_add_to_namespace (info, nsp, name, DEBUG_OBJECT_VARIABLE, linkage);
+  if (n == NULL)
+    return false;
+
+  v = (struct debug_variable *) xmalloc (sizeof *v);
+  memset (v, 0, sizeof *v);
+
+  v->kind = kind;
+  v->type = type;
+  v->val = val;
+
+  n->u.variable = v;
+
+  return true;  
+}
+
+/* Make a type with a given kind and size.  */
+
+/*ARGSUSED*/
+static struct debug_type *
+debug_make_type (info, kind, size)
+     struct debug_handle *info;
+     enum debug_type_kind kind;
+     unsigned int size;
+{
+  struct debug_type *t;
+
+  t = (struct debug_type *) xmalloc (sizeof *t);
+  memset (t, 0, sizeof *t);
+
+  t->kind = kind;
+  t->size = size;
+
+  return t;
+}
+
+/* Make an indirect type which may be used as a placeholder for a type
+   which is referenced before it is defined.  */
+
+debug_type
+debug_make_indirect_type (handle, slot, tag)
+     PTR handle;
+     debug_type *slot;
+     const char *tag;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_indirect_type *i;
+
+  t = debug_make_type (info, DEBUG_KIND_INDIRECT, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  i = (struct debug_indirect_type *) xmalloc (sizeof *i);
+  memset (i, 0, sizeof *i);
+
+  i->slot = slot;
+  i->tag = tag;
+
+  t->u.kindirect = i;
+
+  return t;
+}
+
+/* Make a void type.  There is only one of these.  */
+
+debug_type
+debug_make_void_type (handle)
+     PTR handle;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+
+  return debug_make_type (info, DEBUG_KIND_VOID, 0);
+}
+
+/* Make an integer type of a given size.  The boolean argument is true
+   if the integer is unsigned.  */
+
+debug_type
+debug_make_int_type (handle, size, unsignedp)
+     PTR handle;
+     unsigned int size;
+     boolean unsignedp;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+
+  t = debug_make_type (info, DEBUG_KIND_INT, size);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t->u.kint = unsignedp;
+
+  return t;
+}
+
+/* Make a floating point type of a given size.  FIXME: On some
+   platforms, like an Alpha, you probably need to be able to specify
+   the format.  */
+
+debug_type
+debug_make_float_type (handle, size)
+     PTR handle;
+     unsigned int size;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+
+  return debug_make_type (info, DEBUG_KIND_FLOAT, size);
+}
+
+/* Make a boolean type of a given size.  */
+
+debug_type
+debug_make_bool_type (handle, size)
+     PTR handle;
+     unsigned int size;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+
+  return debug_make_type (info, DEBUG_KIND_BOOL, size);
+}
+
+/* Make a complex type of a given size.  */
+
+debug_type
+debug_make_complex_type (handle, size)
+     PTR handle;
+     unsigned int size;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+
+  return debug_make_type (info, DEBUG_KIND_COMPLEX, size);
+}
+
+/* Make a structure type.  The second argument is true for a struct,
+   false for a union.  The third argument is the size of the struct.
+   The fourth argument is a NULL terminated array of fields.  */
+
+debug_type
+debug_make_struct_type (handle, structp, size, fields)
+     PTR handle;
+     boolean structp;
+     bfd_vma size;
+     debug_field *fields;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_class_type *c;
+
+  t = debug_make_type (info,
+                      structp ? DEBUG_KIND_STRUCT : DEBUG_KIND_UNION,
+                      size);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  c = (struct debug_class_type *) xmalloc (sizeof *c);
+  memset (c, 0, sizeof *c);
+
+  c->fields = fields;
+
+  t->u.kclass = c;
+
+  return t;
+}
+
+/* Make an object type.  The first three arguments after the handle
+   are the same as for debug_make_struct_type.  The next arguments are
+   a NULL terminated array of base classes, a NULL terminated array of
+   methods, the type of the object holding the virtual function table
+   if it is not this object, and a boolean which is true if this
+   object has its own virtual function table.  */
+
+debug_type
+debug_make_object_type (handle, structp, size, fields, baseclasses,
+                       methods, vptrbase, ownvptr)
+     PTR handle;
+     boolean structp;
+     bfd_vma size;
+     debug_field *fields;
+     debug_baseclass *baseclasses;
+     debug_method *methods;
+     debug_type vptrbase;
+     boolean ownvptr;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_class_type *c;
+
+  t = debug_make_type (info,
+                      structp ? DEBUG_KIND_CLASS : DEBUG_KIND_UNION_CLASS,
+                      size);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  c = (struct debug_class_type *) xmalloc (sizeof *c);
+  memset (c, 0, sizeof *c);
+
+  c->fields = fields;
+  c->baseclasses = baseclasses;
+  c->methods = methods;
+  if (ownvptr)
+    c->vptrbase = t;
+  else
+    c->vptrbase = vptrbase;
+
+  t->u.kclass = c;
+
+  return t;
+}
+
+/* Make an enumeration type.  The arguments are a null terminated
+   array of strings, and an array of corresponding values.  */
+
+debug_type
+debug_make_enum_type (handle, names, values)
+     PTR handle;
+     const char **names;
+     bfd_signed_vma *values;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_enum_type *e;
+
+  t = debug_make_type (info, DEBUG_KIND_ENUM, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  e = (struct debug_enum_type *) xmalloc (sizeof *e);
+  memset (e, 0, sizeof *e);
+
+  e->names = names;
+  e->values = values;
+
+  t->u.kenum = e;
+
+  return t;
+}
+
+/* Make a pointer to a given type.  */
+
+debug_type
+debug_make_pointer_type (handle, type)
+     PTR handle;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  if (type->pointer != DEBUG_TYPE_NULL)
+    return type->pointer;
+
+  t = debug_make_type (info, DEBUG_KIND_POINTER, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t->u.kpointer = type;
+
+  type->pointer = t;
+
+  return t;
+}
+
+/* Make a function returning a given type.  FIXME: We should be able
+   to record the parameter types.  */
+
+debug_type
+debug_make_function_type (handle, type)
+     PTR handle;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_function_type *f;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_FUNCTION, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  f = (struct debug_function_type *) xmalloc (sizeof *f);
+  memset (f, 0, sizeof *f);
+
+  f->return_type = type;
+
+  t->u.kfunction = f;
+
+  return t;
+}
+
+/* Make a reference to a given type.  */
+
+debug_type
+debug_make_reference_type (handle, type)
+     PTR handle;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_REFERENCE, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t->u.kreference = type;
+
+  return t;
+}
+
+/* Make a range of a given type from a lower to an upper bound.  */
+
+debug_type
+debug_make_range_type (handle, type, lower, upper)
+     PTR handle;
+     debug_type type;
+     bfd_signed_vma lower;
+     bfd_signed_vma upper;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_range_type *r;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_RANGE, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  r = (struct debug_range_type *) xmalloc (sizeof *r);
+  memset (r, 0, sizeof *r);
+
+  r->type = type;
+  r->lower = lower;
+  r->upper = upper;
+
+  t->u.krange = r;
+
+  return t;
+}
+
+/* Make an array type.  The second argument is the type of an element
+   of the array.  The third argument is the type of a range of the
+   array.  The fourth and fifth argument are the lower and upper
+   bounds, respectively.  The sixth argument is true if this array is
+   actually a string, as in C.  */
+
+debug_type
+debug_make_array_type (handle, element_type, range_type, lower, upper,
+                      stringp)
+     PTR handle;
+     debug_type element_type;
+     debug_type range_type;
+     bfd_signed_vma lower;
+     bfd_signed_vma upper;
+     boolean stringp;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_array_type *a;
+
+  if (element_type == NULL || range_type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_ARRAY, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  a = (struct debug_array_type *) xmalloc (sizeof *a);
+  memset (a, 0, sizeof *a);
+
+  a->element_type = element_type;
+  a->range_type = range_type;
+  a->lower = lower;
+  a->upper = upper;
+  a->stringp = stringp;
+
+  t->u.karray = a;
+
+  return t;
+}
+
+/* Make a set of a given type.  For example, a Pascal set type.  The
+   boolean argument is true if this set is actually a bitstring, as in
+   CHILL.  */
+
+debug_type
+debug_make_set_type (handle, type, bitstringp)
+     PTR handle;
+     debug_type type;
+     boolean bitstringp;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_set_type *s;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_SET, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  s = (struct debug_set_type *) xmalloc (sizeof *s);
+  memset (s, 0, sizeof *s);
+
+  s->type = type;
+  s->bitstringp = bitstringp;
+
+  t->u.kset = s;
+
+  return t;
+}
+
+/* Make a type for a pointer which is relative to an object.  The
+   second argument is the type of the object to which the pointer is
+   relative.  The third argument is the type that the pointer points
+   to.  */
+
+debug_type
+debug_make_offset_type (handle, base_type, target_type)
+     PTR handle;
+     debug_type base_type;
+     debug_type target_type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_offset_type *o;
+
+  if (base_type == NULL || target_type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_OFFSET, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  o = (struct debug_offset_type *) xmalloc (sizeof *o);
+  memset (o, 0, sizeof *o);
+
+  o->base_type = base_type;
+  o->target_type = target_type;
+
+  t->u.koffset = o;
+
+  return t;
+}
+
+/* Make a type for a method function.  The second argument is the
+   return type, the third argument is the domain, and the fourth
+   argument is a NULL terminated array of argument types.  */
+
+debug_type
+debug_make_method_type (handle, return_type, domain_type, arg_types)
+     PTR handle;
+     debug_type return_type;
+     debug_type domain_type;
+     debug_type *arg_types;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_method_type *m;
+
+  if (return_type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_METHOD, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  m = (struct debug_method_type *) xmalloc (sizeof *m);
+  memset (m, 0, sizeof *m);
+
+  m->return_type = return_type;
+  m->domain_type = domain_type;
+  m->arg_types = arg_types;
+
+  t->u.kmethod = m;
+
+  return t;
+}
+
+/* Make a const qualified version of a given type.  */
+
+debug_type
+debug_make_const_type (handle, type)
+     PTR handle;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_CONST, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t->u.kconst = type;
+
+  return t;
+}
+
+/* Make a volatile qualified version of a given type.  */
+
+debug_type
+debug_make_volatile_type (handle, type)
+     PTR handle;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+
+  if (type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_VOLATILE, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t->u.kvolatile = type;
+
+  return t;
+}
+
+/* Make an undefined tagged type.  For example, a struct which has
+   been mentioned, but not defined.  */
+
+debug_type
+debug_make_undefined_tagged_type (handle, name, kind)
+     PTR handle;
+     const char *name;
+     enum debug_type_kind kind;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+
+  if (name == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, kind, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  return debug_tag_type (handle, name, t);
+}
+
+/* Make a base class for an object.  The second argument is the base
+   class type.  The third argument is the bit position of this base
+   class in the object (always 0 unless doing multiple inheritance).
+   The fourth argument is whether this is a virtual class.  The fifth
+   argument is the visibility of the base class.  */
+
+/*ARGSUSED*/
+debug_baseclass
+debug_make_baseclass (handle, type, bitpos, virtual, visibility)
+     PTR handle;
+     debug_type type;
+     bfd_vma bitpos;
+     boolean virtual;
+     enum debug_visibility visibility;
+{     
+  struct debug_baseclass *b;
+
+  b = (struct debug_baseclass *) xmalloc (sizeof *b);
+  memset (b, 0, sizeof *b);
+
+  b->type = type;
+  b->bitpos = bitpos;
+  b->virtual = virtual;
+  b->visibility = visibility;
+
+  return b;
+}
+
+/* Make a field for a struct.  The second argument is the name.  The
+   third argument is the type of the field.  The fourth argument is
+   the bit position of the field.  The fifth argument is the size of
+   the field (it may be zero).  The sixth argument is the visibility
+   of the field.  */
+
+/*ARGSUSED*/
+debug_field
+debug_make_field (handle, name, type, bitpos, bitsize, visibility)
+     PTR handle;
+     const char *name;
+     debug_type type;
+     bfd_vma bitpos;
+     bfd_vma bitsize;
+     enum debug_visibility visibility;
+{
+  struct debug_field *f;
+
+  f = (struct debug_field *) xmalloc (sizeof *f);
+  memset (f, 0, sizeof *f);
+
+  f->name = name;
+  f->type = type;
+  f->static_member = false;
+  f->u.f.bitpos = bitpos;
+  f->u.f.bitsize = bitsize;
+  f->visibility = visibility;
+
+  return f;
+}
+
+/* Make a static member of an object.  The second argument is the
+   name.  The third argument is the type of the member.  The fourth
+   argument is the physical name of the member (i.e., the name as a
+   global variable).  The fifth argument is the visibility of the
+   member.  */
+
+/*ARGSUSED*/
+debug_field
+debug_make_static_member (handle, name, type, physname, visibility)
+     PTR handle;
+     const char *name;
+     debug_type type;
+     const char *physname;
+     enum debug_visibility visibility;
+{
+  struct debug_field *f;
+
+  f = (struct debug_field *) xmalloc (sizeof *f);
+  memset (f, 0, sizeof *f);
+
+  f->name = name;
+  f->type = type;
+  f->static_member = true;
+  f->u.s.physname = physname;
+  f->visibility = visibility;
+
+  return f;
+}
+
+/* Make a method.  The second argument is the name, and the third
+   argument is a NULL terminated array of method variants.  */
+
+/*ARGSUSED*/
+debug_method
+debug_make_method (handle, name, variants)
+     PTR handle;
+     const char *name;
+     debug_method_variant *variants;
+{
+  struct debug_method *m;
+
+  m = (struct debug_method *) xmalloc (sizeof *m);
+  memset (m, 0, sizeof *m);
+
+  m->name = name;
+  m->variants = variants;
+
+  return m;
+}
+
+/* Make a method argument.  The second argument is the real name of
+   the function.  The third argument is the type of the function.  The
+   fourth argument is the visibility.  The fifth argument is whether
+   this is a const function.  The sixth argument is whether this is a
+   volatile function.  The seventh argument is the offset in the
+   virtual function table, if any.  The eighth argument is the virtual
+   function context.  FIXME: Are the const and volatile arguments
+   necessary?  Could we just use debug_make_const_type?  */
+
+/*ARGSUSED*/
+debug_method_variant
+debug_make_method_variant (handle, argtypes, type, visibility, constp,
+                          volatilep, voffset, context)
+     PTR handle;
+     const char *argtypes;
+     debug_type type;
+     enum debug_visibility visibility;
+     boolean constp;
+     boolean volatilep;
+     bfd_vma voffset;
+     debug_type context;
+{
+  struct debug_method_variant *m;
+
+  m = (struct debug_method_variant *) xmalloc (sizeof *m);
+  memset (m, 0, sizeof *m);
+
+  m->argtypes = argtypes;
+  m->type = type;
+  m->visibility = visibility;
+  m->constp = constp;
+  m->volatilep = volatilep;
+  m->voffset = voffset;
+  m->context = context;
+
+  return m;
+}
+
+/* Make a static method argument.  The arguments are the same as for
+   debug_make_method_variant, except that the last two are omitted
+   since a static method can not also be virtual.  */
+
+debug_method_variant
+debug_make_static_method_variant (handle, argtypes, type, visibility,
+                                 constp, volatilep)
+     PTR handle;
+     const char *argtypes;
+     debug_type type;
+     enum debug_visibility visibility;
+     boolean constp;
+     boolean volatilep;
+{
+  struct debug_method_variant *m;
+
+  m = (struct debug_method_variant *) xmalloc (sizeof *m);
+  memset (m, 0, sizeof *m);
+
+  m->argtypes = argtypes;
+  m->type = type;
+  m->visibility = visibility;
+  m->constp = constp;
+  m->volatilep = volatilep;
+  m->voffset = VOFFSET_STATIC_METHOD;
+
+  return m;
+}
+
+/* Name a type.  */
+
+debug_type
+debug_name_type (handle, name, type)
+     PTR handle;
+     const char *name;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_named_type *n;
+  struct debug_name *nm;
+
+  if (name == NULL || type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  t = debug_make_type (info, DEBUG_KIND_NAMED, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  n = (struct debug_named_type *) xmalloc (sizeof *n);
+  memset (n, 0, sizeof *n);
+
+  n->type = type;
+
+  t->u.knamed = n;
+
+  /* We also need to add the name to the current namespace.  */
+
+  nm = debug_add_to_current_namespace (info, name, DEBUG_OBJECT_TYPE,
+                                      DEBUG_LINKAGE_NONE);
+  if (nm == NULL)
+    return false;
+
+  nm->u.type = t;
+
+  n->name = nm;
+
+  return t;
+}
+
+/* Tag a type.  */
+
+debug_type
+debug_tag_type (handle, name, type)
+     PTR handle;
+     const char *name;
+     debug_type type;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_type *t;
+  struct debug_named_type *n;
+  struct debug_name *nm;
+
+  if (name == NULL || type == NULL)
+    return DEBUG_TYPE_NULL;
+
+  if (info->current_file == NULL)
+    {
+      debug_error ("debug_tag_type: no current file");
+      return DEBUG_TYPE_NULL;
+    }
+
+  if (type->kind == DEBUG_KIND_TAGGED)
+    {
+      if (strcmp (type->u.knamed->name->name, name) == 0)
+       return type;
+      debug_error ("debug_tag_type: extra tag attempted");
+      return DEBUG_TYPE_NULL;
+    }
+
+  t = debug_make_type (info, DEBUG_KIND_TAGGED, 0);
+  if (t == NULL)
+    return DEBUG_TYPE_NULL;
+
+  n = (struct debug_named_type *) xmalloc (sizeof *n);
+  memset (n, 0, sizeof *n);
+
+  n->type = type;
+
+  t->u.knamed = n;
+
+  /* We keep a global namespace of tags for each compilation unit.  I
+     don't know if that is the right thing to do.  */
+
+  nm = debug_add_to_namespace (info, &info->current_file->globals, name,
+                              DEBUG_OBJECT_TAG, DEBUG_LINKAGE_NONE);
+  if (nm == NULL)
+    return false;
+
+  nm->u.tag = t;
+
+  n->name = nm;
+
+  return t;
+}
+
+/* Record the size of a given type.  */
+
+/*ARGSUSED*/
+boolean
+debug_record_type_size (handle, type, size)
+     PTR handle;
+     debug_type type;
+     unsigned int size;
+{
+  if (type->size != 0 && type->size != size)
+    fprintf (stderr, "Warning: changing type size from %d to %d\n",
+            type->size, size);
+
+  type->size = size;
+
+  return true;
+}
+
+/* Find a tagged type.  */
+
+debug_type
+debug_find_tagged_type (handle, name, kind)
+     PTR handle;
+     const char *name;
+     enum debug_type_kind kind;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_unit *u;
+
+  /* We search the globals of all the compilation units.  I don't know
+     if this is correct or not.  It would be easy to change.  */
+
+  for (u = info->units; u != NULL; u = u->next)
+    {
+      struct debug_file *f;
+
+      for (f = u->files; f != NULL; f = f->next)
+       {
+         struct debug_name *n;
+
+         if (f->globals != NULL)
+           {
+             for (n = f->globals->list; n != NULL; n = n->next)
+               {
+                 if (n->kind == DEBUG_OBJECT_TAG
+                     && (kind == DEBUG_KIND_VOID
+                         || n->u.tag->kind == kind)
+                     && n->name[0] == name[0]
+                     && strcmp (n->name, name) == 0)
+                   return n->u.tag;
+               }
+           }
+       }
+    }
+
+  return DEBUG_TYPE_NULL;
+}
+
+/* Get the name of a type.  */
+
+/*ARGSUSED*/
+const char *
+debug_get_type_name (handle, type)
+     PTR handle;
+     debug_type type;
+{
+  if (type->kind == DEBUG_KIND_INDIRECT)
+    {
+      if (*type->u.kindirect->slot != NULL)
+       return debug_get_type_name (handle, *type->u.kindirect->slot);
+      return type->u.kindirect->tag;
+    }
+  if (type->kind == DEBUG_KIND_NAMED
+      || type->kind == DEBUG_KIND_TAGGED)
+    return type->u.knamed->name->name;
+  return NULL;
+}
+\f
+/* Write out the debugging information.  This is given a handle to
+   debugging information, and a set of function pointers to call.  */
+
+boolean
+debug_write (handle, fns, fhandle)
+     PTR handle;
+     const struct debug_write_fns *fns;
+     PTR fhandle;
+{
+  struct debug_handle *info = (struct debug_handle *) handle;
+  struct debug_unit *u;
+
+  /* We use a mark to tell whether we have already written out a
+     particular name.  We use an integer, so that we don't have to
+     clear the mark fields if we happen to write out the same
+     information more than once.  */
+  ++info->mark;
+
+  for (u = info->units; u != NULL; u = u->next)
+    {
+      struct debug_file *f;
+      boolean first_file;
+
+      if (! (*fns->start_compilation_unit) (fhandle, u->files->filename))
+       return false;
+
+      first_file = true;
+      for (f = u->files; f != NULL; f = f->next)
+       {
+         struct debug_name *n;
+
+         if (first_file)
+           first_file = false;
+         else
+           {
+             if (! (*fns->start_source) (fhandle, f->filename))
+               return false;
+           }
+
+         if (f->globals != NULL)
+           {
+             for (n = f->globals->list; n != NULL; n = n->next)
+               {
+                 if (! debug_write_name (info, fns, fhandle, n))
+                   return false;
+               }
+           }
+       }
+    }
+
+  return true;
+}
+
+/* Write out an element in a namespace.  */
+
+static boolean
+debug_write_name (info, fns, fhandle, n)
+     struct debug_handle *info;
+     const struct debug_write_fns *fns;
+     PTR fhandle;
+     struct debug_name *n;
+{
+  /* The class_mark field is used to prevent recursively outputting a
+     struct or class.  */
+  ++info->class_mark;
+
+  switch (n->kind)
+    {
+    case DEBUG_OBJECT_TYPE:
+      if (! debug_write_type (info, fns, fhandle, n->u.type, n)
+         || ! (*fns->typdef) (fhandle, n->name))
+       return false;
+      return true;
+    case DEBUG_OBJECT_TAG:
+      if (! debug_write_type (info, fns, fhandle, n->u.tag, n))
+       return false;
+      return (*fns->tag) (fhandle, n->name);
+    case DEBUG_OBJECT_VARIABLE:
+      if (! debug_write_type (info, fns, fhandle, n->u.variable->type,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->variable) (fhandle, n->name, n->u.variable->kind,
+                              n->u.variable->val);
+    case DEBUG_OBJECT_FUNCTION:
+      return debug_write_function (info, fns, fhandle, n->name,
+                                  n->linkage, n->u.function);
+    case DEBUG_OBJECT_INT_CONSTANT:
+      return (*fns->int_constant) (fhandle, n->name, n->u.int_constant);
+    case DEBUG_OBJECT_FLOAT_CONSTANT:
+      return (*fns->float_constant) (fhandle, n->name, n->u.float_constant);
+    case DEBUG_OBJECT_TYPED_CONSTANT:
+      if (! debug_write_type (info, fns, fhandle, n->u.typed_constant->type,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->typed_constant) (fhandle, n->name,
+                                    n->u.typed_constant->val);
+    default:
+      abort ();
+      return false;
+    }
+  /*NOTREACHED*/
+}
+
+/* Write out a type.  */
+
+static boolean
+debug_write_type (info, fns, fhandle, type, name)
+     struct debug_handle *info;
+     const struct debug_write_fns *fns;
+     PTR fhandle;
+     struct debug_type *type;
+     struct debug_name *name;
+{
+  unsigned int i;
+
+  /* If we have a name for this type, just output it.  We only output
+     typedef names after they have been defined.  We output type tags
+     whenever we are not actually defining them.  */
+  if ((type->kind == DEBUG_KIND_NAMED
+       || type->kind == DEBUG_KIND_TAGGED)
+      && (type->u.knamed->name->mark == info->mark
+         || (type->kind == DEBUG_KIND_TAGGED
+             && type->u.knamed->name != name)))
+    {
+      if (type->kind == DEBUG_KIND_NAMED)
+       return (*fns->typedef_type) (fhandle, type->u.knamed->name->name);
+      else
+       return (*fns->tag_type) (fhandle, type->u.knamed->name->name,
+                                type->u.knamed->type->kind);
+    }
+
+  /* Mark the name after we have already looked for a known name, so
+     that we don't just define a type in terms of itself.  We need to
+     mark the name here so that a struct containing a pointer to
+     itself will work.  */
+  if (name != NULL)
+    name->mark = info->mark;
+
+  switch (type->kind)
+    {
+    case DEBUG_KIND_INDIRECT:
+      if (*type->u.kindirect->slot == DEBUG_TYPE_NULL)
+       return (*fns->empty_type) (fhandle);
+      return debug_write_type (info, fns, fhandle, *type->u.kindirect->slot,
+                              (struct debug_name *) NULL);
+    case DEBUG_KIND_VOID:
+      return (*fns->void_type) (fhandle);
+    case DEBUG_KIND_INT:
+      return (*fns->int_type) (fhandle, type->size, type->u.kint);
+    case DEBUG_KIND_FLOAT:
+      return (*fns->float_type) (fhandle, type->size);
+    case DEBUG_KIND_COMPLEX:
+      return (*fns->complex_type) (fhandle, type->size);
+    case DEBUG_KIND_BOOL:
+      return (*fns->bool_type) (fhandle, type->size);
+    case DEBUG_KIND_STRUCT:
+    case DEBUG_KIND_UNION:
+      if (info->class_mark == type->u.kclass->mark)
+       {
+         /* We are currently outputting this struct.  I don't know if
+             this can happen, but it can happen for a class.  */
+         return (*fns->tag_type) (fhandle, "?defining?", type->kind);
+       }
+      type->u.kclass->mark = info->class_mark;
+
+      if (! (*fns->start_struct_type) (fhandle,
+                                      type->kind == DEBUG_KIND_STRUCT,
+                                      type->size))
+       return false;
+      if (type->u.kclass->fields != NULL)
+       {
+         for (i = 0; type->u.kclass->fields[i] != NULL; i++)
+           {
+             struct debug_field *f;
+
+             f = type->u.kclass->fields[i];
+             if (! debug_write_type (info, fns, fhandle, f->type,
+                                     (struct debug_name *) NULL)
+                 || ! (*fns->struct_field) (fhandle, f->name, f->u.f.bitpos,
+                                            f->u.f.bitsize, f->visibility))
+               return false;
+           }
+       }
+      return (*fns->end_struct_type) (fhandle);
+    case DEBUG_KIND_CLASS:
+    case DEBUG_KIND_UNION_CLASS:
+      return debug_write_class_type (info, fns, fhandle, type);
+    case DEBUG_KIND_ENUM:
+      return (*fns->enum_type) (fhandle, type->u.kenum->names,
+                               type->u.kenum->values);
+    case DEBUG_KIND_POINTER:
+      if (! debug_write_type (info, fns, fhandle, type->u.kpointer,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->pointer_type) (fhandle);
+    case DEBUG_KIND_FUNCTION:
+      if (! debug_write_type (info, fns, fhandle,
+                             type->u.kfunction->return_type,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->function_type) (fhandle);
+    case DEBUG_KIND_REFERENCE:
+      if (! debug_write_type (info, fns, fhandle, type->u.kreference,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->reference_type) (fhandle);
+    case DEBUG_KIND_RANGE:
+      if (! debug_write_type (info, fns, fhandle, type->u.krange->type,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->range_type) (fhandle, type->u.krange->lower,
+                                type->u.krange->upper);
+    case DEBUG_KIND_ARRAY:
+      if (! debug_write_type (info, fns, fhandle, type->u.karray->element_type,
+                             (struct debug_name *) NULL)
+         || ! debug_write_type (info, fns, fhandle,
+                                type->u.karray->range_type,
+                                (struct debug_name *) NULL))
+       return false;
+      return (*fns->array_type) (fhandle, type->u.karray->lower,
+                                type->u.karray->upper,
+                                type->u.karray->stringp);
+    case DEBUG_KIND_SET:
+      if (! debug_write_type (info, fns, fhandle, type->u.kset->type,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->set_type) (fhandle, type->u.kset->bitstringp);
+    case DEBUG_KIND_OFFSET:
+      if (! debug_write_type (info, fns, fhandle, type->u.koffset->base_type,
+                             (struct debug_name *) NULL)
+         || ! debug_write_type (info, fns, fhandle,
+                                type->u.koffset->target_type,
+                                (struct debug_name *) NULL))
+       return false;
+      return (*fns->offset_type) (fhandle);
+    case DEBUG_KIND_METHOD:
+      if (! debug_write_type (info, fns, fhandle,
+                             type->u.kmethod->return_type,
+                             (struct debug_name *) NULL))
+       return false;
+      if (type->u.kmethod->arg_types == NULL)
+       i = -1;
+      else
+       {
+         for (i = 0; type->u.kmethod->arg_types[i] != NULL; i++)
+           {
+             if (! debug_write_type (info, fns, fhandle,
+                                     type->u.kmethod->arg_types[i],
+                                     (struct debug_name *) NULL))
+               return false;
+           }
+       }
+      if (type->u.kmethod->domain_type != NULL)
+       {
+         if (! debug_write_type (info, fns, fhandle,
+                                 type->u.kmethod->domain_type,
+                                 (struct debug_name *) NULL))
+           return false;
+       }
+      return (*fns->method_type) (fhandle,
+                                 type->u.kmethod->domain_type != NULL,
+                                 i);
+    case DEBUG_KIND_CONST:
+      if (! debug_write_type (info, fns, fhandle, type->u.kconst,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->const_type) (fhandle);
+    case DEBUG_KIND_VOLATILE:
+      if (! debug_write_type (info, fns, fhandle, type->u.kvolatile,
+                             (struct debug_name *) NULL))
+       return false;
+      return (*fns->volatile_type) (fhandle);
+    case DEBUG_KIND_NAMED:
+    case DEBUG_KIND_TAGGED:
+      return debug_write_type (info, fns, fhandle, type->u.knamed->type,
+                              (struct debug_name *) NULL);
+    default:
+      abort ();
+      return false;
+    }
+}
+
+/* Write out a class type.  */
+
+static boolean
+debug_write_class_type (info, fns, fhandle, type)
+     struct debug_handle *info;
+     const struct debug_write_fns *fns;
+     PTR fhandle;
+     struct debug_type *type;
+{
+  unsigned int i;
+
+  if (info->class_mark == type->u.kclass->mark)
+    {
+      /* We are currently outputting this class.  This can happen when
+         there are methods for an anonymous class.  */
+      return (*fns->tag_type) (fhandle, "?defining?", type->kind);
+    }
+  type->u.kclass->mark = info->class_mark;
+
+  if (type->u.kclass->vptrbase != NULL
+      && type->u.kclass->vptrbase != type)
+    {
+      if (! debug_write_type (info, fns, fhandle, type->u.kclass->vptrbase,
+                             (struct debug_name *) NULL))
+       return false;
+    }
+
+  if (! (*fns->start_class_type) (fhandle,
+                                 type->kind == DEBUG_KIND_CLASS,
+                                 type->size,
+                                 type->u.kclass->vptrbase != NULL,
+                                 type->u.kclass->vptrbase == type))
+    return false;
+  if (type->u.kclass->fields != NULL)
+    {
+      for (i = 0; type->u.kclass->fields[i] != NULL; i++)
+       {
+         struct debug_field *f;
+
+         f = type->u.kclass->fields[i];
+         if (! debug_write_type (info, fns, fhandle, f->type,
+                                 (struct debug_name *) NULL))
+           return false;
+         if (f->static_member)
+           {
+             if (! (*fns->class_static_member) (fhandle, f->name,
+                                                f->u.s.physname,
+                                                f->visibility))
+               return false;
+           }
+         else
+           {
+             if (! (*fns->struct_field) (fhandle, f->name, f->u.f.bitpos,
+                                         f->u.f.bitsize, f->visibility))
+               return false;
+           }
+       }
+    }
+
+  if (type->u.kclass->baseclasses != NULL)
+    {
+      for (i = 0; type->u.kclass->baseclasses[i] != NULL; i++)
+       {
+         struct debug_baseclass *b;
+
+         b = type->u.kclass->baseclasses[i];
+         if (! debug_write_type (info, fns, fhandle, b->type,
+                                 (struct debug_name *) NULL))
+           return false;
+         if (! (*fns->class_baseclass) (fhandle, b->bitpos, b->virtual,
+                                        b->visibility))
+           return false;
+       }
+    }
+
+  if (type->u.kclass->methods != NULL)
+    {
+      for (i = 0; type->u.kclass->methods[i] != NULL; i++)
+       {
+         struct debug_method *m;
+         unsigned int j;
+
+         m = type->u.kclass->methods[i];
+         if (! (*fns->class_start_method) (fhandle, m->name))
+           return false;
+         for (j = 0; m->variants[j] != NULL; j++)
+           {
+             struct debug_method_variant *v;
+
+             v = m->variants[j];
+             if (v->context != NULL)
+               {
+                 if (! debug_write_type (info, fns, fhandle, v->context,
+                                         (struct debug_name *) NULL))
+                   return false;
+               }
+             if (! debug_write_type (info, fns, fhandle, v->type,
+                                     (struct debug_name *) NULL))
+               return false;
+             if (v->voffset != VOFFSET_STATIC_METHOD)
+               {
+                 if (! (*fns->class_method_variant) (fhandle, v->argtypes,
+                                                     v->visibility,
+                                                     v->constp, v->volatilep,
+                                                     v->voffset,
+                                                     v->context != NULL))
+                   return false;
+               }
+             else
+               {
+                 if (! (*fns->class_static_method_variant) (fhandle,
+                                                            v->argtypes,
+                                                            v->visibility,
+                                                            v->constp,
+                                                            v->volatilep))
+                   return false;
+               }
+           }
+         if (! (*fns->class_end_method) (fhandle))
+           return false;
+       }
+    }
+
+  return (*fns->end_class_type) (fhandle);
+}
+
+/* Write out information for a function.  */
+
+static boolean
+debug_write_function (info, fns, fhandle, name, linkage, function)
+     struct debug_handle *info;
+     const struct debug_write_fns *fns;
+     PTR fhandle;
+     const char *name;
+     enum debug_object_linkage linkage;
+     struct debug_function *function;
+{
+  struct debug_parameter *p;
+  struct debug_block *b;
+
+  if (! debug_write_type (info, fns, fhandle, function->return_type,
+                         (struct debug_name *) NULL))
+    return false;
+
+  if (! (*fns->start_function) (fhandle, name,
+                               linkage == DEBUG_LINKAGE_GLOBAL))
+    return false;
+
+  for (p = function->parameters; p != NULL; p = p->next)
+    {
+      if (! debug_write_type (info, fns, fhandle, p->type,
+                             (struct debug_name *) NULL)
+         || ! (*fns->function_parameter) (fhandle, p->name, p->kind, p->val))
+       return false;
+    }
+
+  for (b = function->blocks; b != NULL; b = b->next)
+    {
+      if (! debug_write_block (info, fns, fhandle, b))
+       return false;
+    }
+
+  return (*fns->end_function) (fhandle);
+}
+
+/* Write out information for a block.  */
+
+static boolean
+debug_write_block (info, fns, fhandle, block)
+     struct debug_handle *info;
+     const struct debug_write_fns *fns;
+     PTR fhandle;
+     struct debug_block *block;
+{
+  struct debug_name *n;
+  struct debug_lineno *l;
+  struct debug_block *b;
+
+  if (! (*fns->start_block) (fhandle, block->start))
+    return false;
+
+  if (block->locals != NULL)
+    {
+      for (n = block->locals->list; n != NULL; n = n->next)
+       {
+         if (! debug_write_name (info, fns, fhandle, n))
+           return false;
+       }
+    }
+
+  for (l = block->linenos; l != NULL; l = l->next)
+    {
+      unsigned int i;
+
+      for (i = 0; i < DEBUG_LINENO_COUNT; i++)
+       {
+         if (l->linenos[i] == (unsigned long) -1)
+           break;
+         if (! (*fns->lineno) (fhandle, l->file->filename, l->linenos[i],
+                               l->addrs[i]))
+           return false;
+       }
+    }
+
+  for (b = block->children; b != NULL; b = b->next)
+    {
+      if (! debug_write_block (info, fns, fhandle, b))
+       return false;
+    }
+
+  return (*fns->end_block) (fhandle, block->end);
+}
diff --git a/binutils/debug.h b/binutils/debug.h
new file mode 100644 (file)
index 0000000..6682e47
--- /dev/null
@@ -0,0 +1,701 @@
+/* debug.h -- Describe generic 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.  */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+/* This header file describes a generic debugging information format.
+   We may eventually have readers which convert different formats into
+   this generic format, and writers which write it out.  The initial
+   impetus for this was writing a convertor from stabs to HP IEEE-695
+   debugging format.  */
+
+/* Different kinds of types.  */
+
+enum debug_type_kind
+{
+  /* Indirect via a pointer.  */
+  DEBUG_KIND_INDIRECT,
+  /* Void.  */
+  DEBUG_KIND_VOID,
+  /* Integer.  */
+  DEBUG_KIND_INT,
+  /* Floating point.  */
+  DEBUG_KIND_FLOAT,
+  /* Complex.  */
+  DEBUG_KIND_COMPLEX,
+  /* Boolean.  */
+  DEBUG_KIND_BOOL,
+  /* Struct.  */
+  DEBUG_KIND_STRUCT,
+  /* Union.  */
+  DEBUG_KIND_UNION,
+  /* Class.  */
+  DEBUG_KIND_CLASS,
+  /* Union class (can this really happen?).  */
+  DEBUG_KIND_UNION_CLASS,
+  /* Enumeration type.  */
+  DEBUG_KIND_ENUM,
+  /* Pointer.  */
+  DEBUG_KIND_POINTER,
+  /* Function.  */
+  DEBUG_KIND_FUNCTION,
+  /* Reference.  */
+  DEBUG_KIND_REFERENCE,
+  /* Range.  */
+  DEBUG_KIND_RANGE,
+  /* Array.  */
+  DEBUG_KIND_ARRAY,
+  /* Set.  */
+  DEBUG_KIND_SET,
+  /* Based pointer.  */
+  DEBUG_KIND_OFFSET,
+  /* Method.  */
+  DEBUG_KIND_METHOD,
+  /* Const qualified type.  */
+  DEBUG_KIND_CONST,
+  /* Volatile qualified type.  */
+  DEBUG_KIND_VOLATILE,
+  /* Named type.  */
+  DEBUG_KIND_NAMED,
+  /* Tagged type.  */
+  DEBUG_KIND_TAGGED
+};
+
+/* Different kinds of variables.  */
+
+enum debug_var_kind
+{
+  /* A global variable.  */
+  DEBUG_GLOBAL,
+  /* A static variable.  */
+  DEBUG_STATIC,
+  /* A local static variable.  */
+  DEBUG_LOCAL_STATIC,
+  /* A local variable.  */
+  DEBUG_LOCAL,
+  /* A register variable.  */
+  DEBUG_REGISTER
+};
+
+/* Different kinds of function parameters.  */
+
+enum debug_parm_kind
+{
+  /* A stack based parameter.  */
+  DEBUG_PARM_STACK,
+  /* A register parameter.  */
+  DEBUG_PARM_REG,
+  /* A stack based reference parameter.  */
+  DEBUG_PARM_REFERENCE,
+  /* A register reference parameter.  */
+  DEBUG_PARM_REF_REG
+};
+
+/* Different kinds of visibility.  */
+
+enum debug_visibility
+{
+  /* A public field (e.g., a field in a C struct).  */
+  DEBUG_VISIBILITY_PUBLIC,
+  /* A protected field.  */
+  DEBUG_VISIBILITY_PROTECTED,
+  /* A private field.  */
+  DEBUG_VISIBILITY_PRIVATE,
+  /* A field which should be ignored.  */
+  DEBUG_VISIBILITY_IGNORE
+};
+
+/* A type.  */
+
+typedef struct debug_type *debug_type;
+
+#define DEBUG_TYPE_NULL ((debug_type) NULL)
+
+/* A field in a struct or union.  */
+
+typedef struct debug_field *debug_field;
+
+#define DEBUG_FIELD_NULL ((debug_field) NULL)
+
+/* A base class for an object.  */
+
+typedef struct debug_baseclass *debug_baseclass;
+
+#define DEBUG_BASECLASS_NULL ((debug_baseclass) NULL)
+
+/* A method of an object.  */
+
+typedef struct debug_method *debug_method;
+
+#define DEBUG_METHOD_NULL ((debug_method) NULL)
+
+/* The arguments to a method function of an object.  These indicate
+   which method to run.  */
+
+typedef struct debug_method_variant *debug_method_variant;
+
+#define DEBUG_METHOD_VARIANT_NULL ((debug_method_variant) NULL)
+
+/* This structure is passed to debug_write.  It holds function
+   pointers that debug_write will call based on the accumulated
+   debugging information.  */
+
+struct debug_write_fns
+{
+  /* This is called at the start of each new compilation unit with the
+     name of the main file in the new unit.  */
+  boolean (*start_compilation_unit) PARAMS ((PTR, const char *));
+
+  /* This is called at the start of each source file within a
+     compilation unit, before outputting any global information for
+     that file.  The argument is the name of the file.  */
+  boolean (*start_source) PARAMS ((PTR, const char *));
+
+  /* Each writer must keep a stack of types.  */
+
+  /* Push an empty type onto the type stack.  This type can appear if
+     there is a reference to a type which is never defined.  */
+  boolean (*empty_type) PARAMS ((PTR));
+
+  /* Push a void type onto the type stack.  */
+  boolean (*void_type) PARAMS ((PTR));
+
+  /* Push an integer type onto the type stack, given the size and
+     whether it is unsigned.  */
+  boolean (*int_type) PARAMS ((PTR, unsigned int, boolean));
+
+  /* Push a floating type onto the type stack, given the size.  */
+  boolean (*float_type) PARAMS ((PTR, unsigned int));
+
+  /* Push a complex type onto the type stack, given the size.  */
+  boolean (*complex_type) PARAMS ((PTR, unsigned int));
+
+  /* Push a boolean type onto the type stack, given the size.  */
+  boolean (*bool_type) PARAMS ((PTR, unsigned int));
+
+  /* Push an enum type onto the type stack, given a NULL terminated
+     array of names and the associated values.  */
+  boolean (*enum_type) PARAMS ((PTR, const char **, bfd_signed_vma *));
+
+  /* Pop the top type on the type stack, and push a pointer to that
+     type onto the type stack.  */
+  boolean (*pointer_type) PARAMS ((PTR));
+
+  /* Pop the top type on the type stack, and push a function returning
+     that type onto the type stack.  */
+  boolean (*function_type) PARAMS ((PTR));
+
+  /* Pop the top type on the type stack, and push a reference to that
+     type onto the type stack.  */
+  boolean (*reference_type) PARAMS ((PTR));
+
+  /* Pop the top type on the type stack, and push a range of that type
+     with the given lower and upper bounds onto the type stack.  */
+  boolean (*range_type) PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma));
+
+  /* Push an array type onto the type stack.  The top type on the type
+     stack is the range, and the next type on the type stack is the
+     element type.  These should be popped before the array type is
+     pushed.  The arguments are the lower bound, the upper bound, and
+     whether the array is a string.  */
+  boolean (*array_type) PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma,
+                                boolean));
+
+  /* Pop the top type on the type stack, and push a set of that type
+     onto the type stack.  The argument indicates whether this set is
+     a bitstring.  */
+  boolean (*set_type) PARAMS ((PTR, boolean));
+
+  /* Push an offset type onto the type stack.  The top type on the
+     type stack is the target type, and the next type on the type
+     stack is the base type.  These should be popped before the offset
+     type is pushed.  */
+  boolean (*offset_type) PARAMS ((PTR));
+
+  /* Push a method type onto the type stack.  If the second argument
+     is true, the top type on the stack is the class to which the
+     method belongs; otherwise, the class must be determined by the
+     class to which the method is attached.  The third argument is the
+     number of argument types; these are pushed onto the type stack in
+     reverse order (the first type popped is the last argument to the
+     method).  An argument type of -1 means that no argument in
+     formation is available.  The next type on the type stack below
+     the domain and the argument types is the return type of the
+     method.  All these types must be poppsed, and then the method
+     type must be pushed.  */
+  boolean (*method_type) PARAMS ((PTR, boolean, int));
+
+  /* Pop the top type off the type stack, and push a const qualified
+     version of that type onto the type stack.  */
+  boolean (*const_type) PARAMS ((PTR));
+
+  /* Pop the top type off the type stack, and push a volatile
+     qualified version of that type onto the type stack.  */
+  boolean (*volatile_type) PARAMS ((PTR));
+
+  /* Start building a struct.  This is followed by calls to the
+     struct_field function, and finished by a call to the
+     end_struct_type function.  The boolean argument is true for a
+     struct, false for a union.  The unsigned int argument is the
+     size.  */
+  boolean (*start_struct_type) PARAMS ((PTR, boolean, unsigned int));
+
+  /* Add a field to the struct type currently being built.  The type
+     of the field should be popped off the type stack.  The arguments
+     are the name, the bit position, the bit size (may be zero if the
+     field is not packed), and the visibility.  */
+  boolean (*struct_field) PARAMS ((PTR, const char *, bfd_vma, bfd_vma,
+                                  enum debug_visibility));
+
+  /* Finish building a struct, and push it onto the type stack.  */
+  boolean (*end_struct_type) PARAMS ((PTR));
+
+  /* Start building a class.  This is followed by calls to several
+     functions: struct_field, class_static_member, class_baseclass,
+     class_start_method, class_method_variant,
+     class_static_method_variant, and class_end_method.  The class is
+     finished by a call to end_class_type.  The boolean argument is
+     true for a struct, false for a union.  The next argument is the
+     size.  The next argument is true if there is a virtual function
+     table; if there is, the next argument is true if the virtual
+     function table can be found in the type itself, and is false if
+     the type of the object holding the virtual function table should
+     be popped from the type stack.  */
+  boolean (*start_class_type) PARAMS ((PTR, boolean, unsigned int,
+                                      boolean, boolean));
+
+  /* Add a static member to the class currently being built.  The
+     arguments are the field name, the physical name, and the
+     visibility.  */
+  boolean (*class_static_member) PARAMS ((PTR, const char *, const char *,
+                                         enum debug_visibility));
+  
+  /* Add a baseclass to the class currently being built.  The type of
+     the baseclass must be popped off the type stack.  The arguments
+     are the bit position, whether the class is virtual, and the
+     visibility.  */
+  boolean (*class_baseclass) PARAMS ((PTR, bfd_vma, boolean,
+                                     enum debug_visibility));
+
+  /* Start adding a method to the class currently being built.  This
+     is followed by calls to class_method_variant and
+     class_static_method_variant to describe different variants of the
+     method which take different arguments.  The method is finished
+     with a call to class_end_method.  The argument is the method
+     name.  */
+  boolean (*class_start_method) PARAMS ((PTR, const char *));
+
+  /* Describe a variant to the class method currently being built.
+     The type of the variant must be popped off the type stack.  The
+     second argument is a string which is either the physical name of
+     the function or describes the argument types; see the comment for
+     debug_make_method variant.  The following arguments are the
+     visibility, whether the variant is const, whether the variant is
+     volatile, the offset in the virtual function table, and whether
+     the context is on the type stack (below the variant type).  */
+  boolean (*class_method_variant) PARAMS ((PTR, const char *,
+                                          enum debug_visibility,
+                                          boolean, boolean,
+                                          bfd_vma, boolean));
+
+  /* Describe a static variant to the class method currently being
+     built.  The arguments are the same as for class_method_variant,
+     except that the last two arguments are omitted.  */
+  boolean (*class_static_method_variant) PARAMS ((PTR, const char *,
+                                                 enum debug_visibility,
+                                                 boolean, boolean));
+
+  /* Finish describing a class method.  */
+  boolean (*class_end_method) PARAMS ((PTR));
+
+  /* Finish describing a class, and push it onto the type stack.  */
+  boolean (*end_class_type) PARAMS ((PTR));
+
+  /* Push a type on the stack which was given a name by an earlier
+     call to typdef.  */
+  boolean (*typedef_type) PARAMS ((PTR, const char *));
+
+  /* Push a type on the stack which was given a name by an earlier
+     call to tag.  */
+  boolean (*tag_type) PARAMS ((PTR, const char *, enum debug_type_kind));
+
+  /* Pop the type stack, and typedef it to the given name.  */
+  boolean (*typdef) PARAMS ((PTR, const char *));
+
+  /* Pop the type stack, and declare it as a tagged struct or union or
+     enum or whatever.  */
+  boolean (*tag) PARAMS ((PTR, const char *));
+
+  /* This is called to record a named integer constant.  */
+  boolean (*int_constant) PARAMS ((PTR, const char *, bfd_vma));
+
+  /* This is called to record a named floating point constant.  */
+  boolean (*float_constant) PARAMS ((PTR, const char *, double));
+
+  /* This is called to record a typed integer constant.  The type is
+     popped off the type stack.  */
+  boolean (*typed_constant) PARAMS ((PTR, const char *, bfd_vma));
+
+  /* This is called to record a variable.  The type is popped off the
+     type stack.  */
+  boolean (*variable) PARAMS ((PTR, const char *, enum debug_var_kind,
+                              bfd_vma));
+
+  /* Start writing out a function.  The return type must be popped off
+     the stack.  The boolean is true if the function is global.  This
+     is followed by calls to function_parameter, followed by block
+     information.  */
+  boolean (*start_function) PARAMS ((PTR, const char *, boolean));
+
+  /* Record a function parameter for the current function.  The type
+     must be popped off the stack.  */
+  boolean (*function_parameter) PARAMS ((PTR, const char *,
+                                        enum debug_parm_kind, bfd_vma));
+
+  /* Start writing out a block.  There is at least one top level block
+     per function.  Blocks may be nested.  The argument is the
+     starting address of the block.  */
+  boolean (*start_block) PARAMS ((PTR, bfd_vma));
+
+  /* Record line number information for the current block.  */
+  boolean (*lineno) PARAMS ((PTR, const char *, unsigned long, bfd_vma));
+
+  /* Finish writing out a block.  The argument is the ending address
+     of the block.  */
+  boolean (*end_block) PARAMS ((PTR, bfd_vma));
+
+  /* Finish writing out a function.  */
+  boolean (*end_function) PARAMS ((PTR));
+};
+
+/* Exported functions.  */
+
+/* The first argument to most of these functions is a handle.  This
+   handle is returned by the debug_init function.  The purpose of the
+   handle is to permit the debugging routines to not use static
+   variables, and hence to be reentrant.  This would be useful for a
+   program which wanted to handle two executables simultaneously.  */
+
+/* Return a debugging handle.  */
+
+extern PTR debug_init PARAMS ((void));
+
+/* Set the source filename.  This implicitly starts a new compilation
+   unit.  */
+
+extern boolean debug_set_filename PARAMS ((PTR, const char *));
+
+/* Append a string to the source filename.  */
+
+extern boolean debug_append_filename PARAMS ((PTR, const char *));
+
+/* Change source files to the given file name.  This is used for
+   include files in a single compilation unit.  */
+
+extern boolean debug_start_source PARAMS ((PTR, const char *));
+
+/* Record a function definition.  This implicitly starts a function
+   block.  The debug_type argument is the type of the return value.
+   The boolean indicates whether the function is globally visible.
+   The bfd_vma is the address of the start of the function.  Currently
+   the parameter types are specified by calls to
+   debug_record_parameter.  */
+
+extern boolean debug_record_function
+  PARAMS ((PTR, const char *, debug_type, boolean, bfd_vma));
+
+/* Record a parameter for the current function.  */
+
+extern boolean debug_record_parameter
+  PARAMS ((PTR, const char *, debug_type, enum debug_parm_kind, bfd_vma));
+
+/* End a function definition.  The argument is the address where the
+   function ends.  */
+
+extern boolean debug_end_function PARAMS ((PTR, bfd_vma));
+
+/* Start a block in a function.  All local information will be
+   recorded in this block, until the matching call to debug_end_block.
+   debug_start_block and debug_end_block may be nested.  The argument
+   is the address at which this block starts.  */
+
+extern boolean debug_start_block PARAMS ((PTR, bfd_vma));
+
+/* Finish a block in a function.  This matches the call to
+   debug_start_block.  The argument is the address at which this block
+   ends.  */
+
+extern boolean debug_end_block PARAMS ((PTR, bfd_vma));
+
+/* Associate a line number in the current source file and function
+   with a given address.  */
+
+extern boolean debug_record_line PARAMS ((PTR, unsigned long, bfd_vma));
+
+/* Start a named common block.  This is a block of variables that may
+   move in memory.  */
+
+extern boolean debug_start_common_block PARAMS ((PTR, const char *));
+
+/* End a named common block.  */
+
+extern boolean debug_end_common_block PARAMS ((PTR, const char *));
+
+/* Record a named integer constant.  */
+
+extern boolean debug_record_int_const PARAMS ((PTR, const char *, bfd_vma));
+
+/* Record a named floating point constant.  */
+
+extern boolean debug_record_float_const PARAMS ((PTR, const char *, double));
+
+/* Record a typed constant with an integral value.  */
+
+extern boolean debug_record_typed_const
+  PARAMS ((PTR, const char *, debug_type, bfd_vma));
+
+/* Record a label.  */
+
+extern boolean debug_record_label
+  PARAMS ((PTR, const char *, debug_type, bfd_vma));
+
+/* Record a variable.  */
+
+extern boolean debug_record_variable
+  PARAMS ((PTR, const char *, debug_type, enum debug_var_kind, bfd_vma));
+
+/* Make an indirect type.  The first argument is a pointer to the
+   location where the real type will be placed.  The second argument
+   is the type tag, if there is one; this may be NULL; the only
+   purpose of this argument is so that debug_get_type_name can return
+   something useful.  This function may be used when a type is
+   referenced before it is defined.  */
+
+extern debug_type debug_make_indirect_type
+  PARAMS ((PTR, debug_type *, const char *));
+
+/* Make a void type.  */
+
+extern debug_type debug_make_void_type PARAMS ((PTR));
+
+/* Make an integer type of a given size.  The boolean argument is true
+   if the integer is unsigned.  */
+
+extern debug_type debug_make_int_type PARAMS ((PTR, unsigned int, boolean));
+
+/* Make a floating point type of a given size.  FIXME: On some
+   platforms, like an Alpha, you probably need to be able to specify
+   the format.  */
+
+extern debug_type debug_make_float_type PARAMS ((PTR, unsigned int));
+
+/* Make a boolean type of a given size.  */
+
+extern debug_type debug_make_bool_type PARAMS ((PTR, unsigned int));
+
+/* Make a complex type of a given size.  */
+
+extern debug_type debug_make_complex_type PARAMS ((PTR, unsigned int));
+
+/* Make a structure type.  The second argument is true for a struct,
+   false for a union.  The third argument is the size of the struct.
+   The fourth argument is a NULL terminated array of fields.  */
+
+extern debug_type debug_make_struct_type
+  PARAMS ((PTR, boolean, bfd_vma, debug_field *));
+
+/* Make an object type.  The first three arguments after the handle
+   are the same as for debug_make_struct_type.  The next arguments are
+   a NULL terminated array of base classes, a NULL terminated array of
+   methods, the type of the object holding the virtual function table
+   if it is not this object, and a boolean which is true if this
+   object has its own virtual function table.  */
+
+extern debug_type debug_make_object_type
+  PARAMS ((PTR, boolean, bfd_vma, debug_field *, debug_baseclass *,
+          debug_method *, debug_type, boolean));
+
+/* Make an enumeration type.  The arguments are a null terminated
+   array of strings, and an array of corresponding values.  */
+
+extern debug_type debug_make_enum_type
+  PARAMS ((PTR, const char **, bfd_signed_vma *));
+
+/* Make a pointer to a given type.  */
+
+extern debug_type debug_make_pointer_type
+  PARAMS ((PTR, debug_type));
+
+/* Make a function returning a given type.  FIXME: We should be able
+   to record the parameter types.  */
+
+extern debug_type debug_make_function_type PARAMS ((PTR, debug_type));
+
+/* Make a reference to a given type.  */
+
+extern debug_type debug_make_reference_type PARAMS ((PTR, debug_type));
+
+/* Make a range of a given type from a lower to an upper bound.  */
+
+extern debug_type debug_make_range_type
+  PARAMS ((PTR, debug_type, bfd_signed_vma, bfd_signed_vma));
+
+/* Make an array type.  The second argument is the type of an element
+   of the array.  The third argument is the type of a range of the
+   array.  The fourth and fifth argument are the lower and upper
+   bounds, respectively (if the bounds are not known, lower should be
+   0 and upper should be -1).  The sixth argument is true if this
+   array is actually a string, as in C.  */
+
+extern debug_type debug_make_array_type
+  PARAMS ((PTR, debug_type, debug_type, bfd_signed_vma, bfd_signed_vma,
+          boolean));
+
+/* Make a set of a given type.  For example, a Pascal set type.  The
+   boolean argument is true if this set is actually a bitstring, as in
+   CHILL.  */
+
+extern debug_type debug_make_set_type PARAMS ((PTR, debug_type, boolean));
+
+/* Make a type for a pointer which is relative to an object.  The
+   second argument is the type of the object to which the pointer is
+   relative.  The third argument is the type that the pointer points
+   to.  */
+
+extern debug_type debug_make_offset_type
+  PARAMS ((PTR, debug_type, debug_type));
+
+/* Make a type for a method function.  The second argument is the
+   return type, the third argument is the domain, and the fourth
+   argument is a NULL terminated array of argument types.  The domain
+   and the argument array may be NULL, in which case this is a stub
+   method and that information is not available.  Stabs debugging uses
+   this, and gets the argument types from the mangled name.  */
+
+extern debug_type debug_make_method_type
+  PARAMS ((PTR, debug_type, debug_type, debug_type *));
+
+/* Make a const qualified version of a given type.  */
+
+extern debug_type debug_make_const_type PARAMS ((PTR, debug_type));
+
+/* Make a volatile qualified version of a given type.  */
+
+extern debug_type debug_make_volatile_type PARAMS ((PTR, debug_type));
+
+/* Make an undefined tagged type.  For example, a struct which has
+   been mentioned, but not defined.  */
+
+extern debug_type debug_make_undefined_tagged_type
+  PARAMS ((PTR, const char *, enum debug_type_kind));
+
+/* Make a base class for an object.  The second argument is the base
+   class type.  The third argument is the bit position of this base
+   class in the object (always 0 unless doing multiple inheritance).
+   The fourth argument is whether this is a virtual class.  The fifth
+   argument is the visibility of the base class.  */
+
+extern debug_baseclass debug_make_baseclass
+  PARAMS ((PTR, debug_type, bfd_vma, boolean, enum debug_visibility));
+
+/* Make a field for a struct.  The second argument is the name.  The
+   third argument is the type of the field.  The fourth argument is
+   the bit position of the field.  The fifth argument is the size of
+   the field (it may be zero).  The sixth argument is the visibility
+   of the field.  */
+
+extern debug_field debug_make_field
+  PARAMS ((PTR, const char *, debug_type, bfd_vma, bfd_vma,
+          enum debug_visibility));
+
+/* Make a static member of an object.  The second argument is the
+   name.  The third argument is the type of the member.  The fourth
+   argument is the physical name of the member (i.e., the name as a
+   global variable).  The fifth argument is the visibility of the
+   member.  */
+
+extern debug_field debug_make_static_member
+  PARAMS ((PTR, const char *, debug_type, const char *,
+          enum debug_visibility));
+
+/* Make a method.  The second argument is the name, and the third
+   argument is a NULL terminated array of method variants.  Each
+   method variant is a method with this name but with different
+   argument types.  */
+
+extern debug_method debug_make_method
+  PARAMS ((PTR, const char *, debug_method_variant *));
+
+/* Make a method variant.  The second argument is either the physical
+   name of the function, or the encoded argument types, depending upon
+   whether the third argument specifies the argument types or not.
+   The third argument is the type of the function.  The fourth
+   argument is the visibility.  The fifth argument is whether this is
+   a const function.  The sixth argument is whether this is a volatile
+   function.  The seventh argument is the offset in the virtual
+   function table, if any.  The eighth argument is the virtual
+   function context.  FIXME: Are the const and volatile arguments
+   necessary?  Could we just use debug_make_const_type?  The handling
+   of the second argument is biased toward the way that stabs works.  */
+
+extern debug_method_variant debug_make_method_variant
+  PARAMS ((PTR, const char *, debug_type, enum debug_visibility, boolean,
+          boolean, bfd_vma, debug_type));
+
+/* Make a static method argument.  The arguments are the same as for
+   debug_make_method_variant, except that the last two are omitted
+   since a static method can not also be virtual.  */
+
+extern debug_method_variant debug_make_static_method_variant
+  PARAMS ((PTR, const char *, debug_type, enum debug_visibility, boolean,
+          boolean));
+
+/* Name a type.  This returns a new type with an attached name.  */
+
+extern debug_type debug_name_type PARAMS ((PTR, const char *, debug_type));
+
+/* Give a tag to a type, such as a struct or union.  This returns a
+   new type with an attached tag.  */
+
+extern debug_type debug_tag_type PARAMS ((PTR, const char *, debug_type));
+
+/* Record the size of a given type.  */
+
+extern boolean debug_record_type_size PARAMS ((PTR, debug_type, unsigned int));
+
+/* Find a tagged type.  */
+
+extern debug_type debug_find_tagged_type
+  PARAMS ((PTR, const char *, enum debug_type_kind));
+
+/* Get the name of a type.  */
+
+extern const char *debug_get_type_name PARAMS ((PTR, debug_type));
+
+/* Write out the recorded debugging information.  This takes a set of
+   function pointers which are called to do the actual writing.  The
+   first PTR is the debugging handle.  The second PTR is a handle
+   which is passed to the functions.  */
+
+extern boolean debug_write PARAMS ((PTR, const struct debug_write_fns *, PTR));
+
+#endif /* DEBUG_H */
index 1f39a35842d531d9e7b63e497ed09038834fd596..7c7e93ae1f70ff535713aeb9dce045b8528cf113 100644 (file)
@@ -21,6 +21,7 @@ objdump \- display information from object files.
 .RB " | " "\-\-target="\c
 .I bfdname\c
 \&\|] 
+.RB "[\|" \-\-debugging "\|]" 
 .RB "[\|" \-d | \-\-disassemble "\|]" 
 .RB "[\|" \-D | \-\-disassemble-all "\|]" 
 .RB "[\|" \-f | \-\-file\-headers "\|]"
@@ -125,6 +126,12 @@ formats available with the `\|\c
 .B \-i\c
 \|' option.
 
+.TP
+.B \-\-debugging
+Display debugging information.  This attempts to parse debugging
+information stored in the file and print it out using a C like syntax.
+Only certain types of debugging information have been implemented.
+
 .TP
 .B \-d
 .TP
index 46783a0c3de8b0f3acbe5c41040a78879602bf20..b419346bb11863304acf28426a848e18993d9097 100644 (file)
@@ -21,11 +21,11 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "getopt.h"
 #include "progress.h"
 #include "bucomm.h"
-#include <sys/types.h>
-#include <stdio.h>
 #include <ctype.h>
 #include "dis-asm.h"
 #include "libiberty.h"
+#include "debug.h"
+#include "budbg.h"
 
 /* Internal headers for the ELF .stab-dump code - sorry.  */
 #define        BYTES_IN_WORD   32
@@ -60,6 +60,7 @@ char *only;                   /* -j secname */
 int wide_output;               /* -w */
 bfd_vma start_address = (bfd_vma) -1; /* --start-address */
 bfd_vma stop_address = (bfd_vma) -1;  /* --stop-address */
+int dump_debugging;            /* --debugging */
 
 /* Extra info to pass to the disassembler address printing function.  */
 struct objdump_disasm_info {
@@ -120,6 +121,9 @@ objdump_print_address PARAMS ((bfd_vma, struct disassemble_info *));
 
 static void
 show_line PARAMS ((bfd *, asection *, bfd_vma));
+
+static const char *
+endian_string PARAMS ((enum bfd_endian));
 \f
 void
 usage (stream, status)
@@ -128,7 +132,7 @@ usage (stream, status)
 {
   fprintf (stream, "\
 Usage: %s [-ahifdDprRtTxsSlw] [-b bfdname] [-m machine] [-j section-name]\n\
-       [--archive-headers] [--target=bfdname] [--disassemble]\n\
+       [--archive-headers] [--target=bfdname] [--debugging] [--disassemble]\n\
        [--disassemble-all] [--file-headers] [--section-headers] [--headers]\n\
        [--info] [--section=section-name] [--line-numbers] [--source]\n",
           program_name);
@@ -153,6 +157,7 @@ static struct option long_options[]=
   {"private-headers", no_argument, NULL, 'p'},
   {"architecture", required_argument, NULL, 'm'},
   {"archive-headers", no_argument, NULL, 'a'},
+  {"debugging", no_argument, &dump_debugging, 1},
   {"disassemble", no_argument, NULL, 'd'},
   {"disassemble-all", no_argument, NULL, 'D'},
   {"dynamic-reloc", no_argument, NULL, 'R'},
@@ -321,7 +326,7 @@ remove_useless_symbols (symbols, count)
   return out_ptr - symbols;
 }
 
-/* Sort symbols into value order. */
+/* Sort symbols into value order.  */
 
 static int 
 compare_symbols (ap, bp)
@@ -333,6 +338,7 @@ compare_symbols (ap, bp)
   const char *an, *bn;
   size_t anl, bnl;
   boolean af, bf;
+  flagword aflags, bflags;
 
   if (bfd_asymbol_value (a) > bfd_asymbol_value (b))
     return 1;
@@ -381,6 +387,27 @@ compare_symbols (ap, bp)
   if (! af && bf)
     return -1;
 
+  /* Finally, try to sort global symbols before local symbols before
+     debugging symbols.  */
+
+  aflags = a->flags;
+  bflags = b->flags;
+
+  if ((aflags & BSF_DEBUGGING) != (bflags & BSF_DEBUGGING))
+    {
+      if ((aflags & BSF_DEBUGGING) != 0)
+       return 1;
+      else
+       return -1;
+    }
+  if ((aflags & BSF_LOCAL) != (bflags & BSF_LOCAL))
+    {
+      if ((aflags & BSF_LOCAL) != 0)
+       return 1;
+      else
+       return -1;
+    }
+
   return 0;
 }
 
@@ -432,9 +459,6 @@ objdump_print_address (vma, info)
      bfd_vma vma;
      struct disassemble_info *info;
 {
-  /* @@ For relocateable files, should filter out symbols belonging to
-     the wrong section.  Unfortunately, not enough information is supplied
-     to this routine to determine the correct section in all cases.  */
   /* @@ Would it speed things up to cache the last two symbols returned,
      and maybe their address ranges?  For many processors, only one memory
      operand can be present at a time, so the 2-entry cache wouldn't be
@@ -471,43 +495,14 @@ objdump_print_address (vma, info)
     }
 
   /* The symbol we want is now in min, the low end of the range we
-     were searching.  */
+     were searching.  If there are several symbols with the same
+     value, we want the first one.  */
   thisplace = min;
   while (thisplace > 0
         && (bfd_asymbol_value (sorted_syms[thisplace])
             == bfd_asymbol_value (sorted_syms[thisplace - 1])))
     --thisplace;
 
-  {
-    /* If this symbol isn't global, search for one with the same value
-       that is.  */
-    bfd_vma val = bfd_asymbol_value (sorted_syms[thisplace]);
-    long i;
-    if (sorted_syms[thisplace]->flags & (BSF_LOCAL|BSF_DEBUGGING))
-      for (i = thisplace - 1; i >= 0; i--)
-       {
-         if (bfd_asymbol_value (sorted_syms[i]) == val
-             && (!(sorted_syms[i]->flags & (BSF_LOCAL|BSF_DEBUGGING))
-                 || ((sorted_syms[thisplace]->flags & BSF_DEBUGGING)
-                     && !(sorted_syms[i]->flags & BSF_DEBUGGING))))
-           {
-             thisplace = i;
-             break;
-           }
-       }
-    if (sorted_syms[thisplace]->flags & (BSF_LOCAL|BSF_DEBUGGING))
-      for (i = thisplace + 1; i < sorted_symcount; i++)
-       {
-         if (bfd_asymbol_value (sorted_syms[i]) == val
-             && (!(sorted_syms[i]->flags & (BSF_LOCAL|BSF_DEBUGGING))
-                 || ((sorted_syms[thisplace]->flags & BSF_DEBUGGING)
-                     && !(sorted_syms[i]->flags & BSF_DEBUGGING))))
-           {
-             thisplace = i;
-             break;
-           }
-       }
-  }
   {
     /* If the file is relocateable, and the symbol could be from this
        section, prefer a symbol from this section over symbols from
@@ -1321,6 +1316,18 @@ display_bfd (abfd)
     dump_data (abfd);
   if (disassemble)
     disassemble_data (abfd);
+  if (dump_debugging)
+    {
+      PTR dhandle;
+
+      dhandle = read_debugging_info (abfd);
+      if (dhandle != NULL)
+       {
+         if (! print_debugging_info (stdout, dhandle))
+           fprintf (stderr, "%s: printing debugging information failed\n",
+                    bfd_get_filename (abfd));
+       }
+    }
   if (syms)
     {
       free (syms);
@@ -1678,6 +1685,18 @@ dump_reloc_set (abfd, relpp, relcount)
 #define L_tmpnam 25
 #endif
 
+static const char *
+endian_string (endian)
+     enum bfd_endian endian;
+{
+  if (endian == BFD_ENDIAN_BIG)
+    return "big endian";
+  else if (endian == BFD_ENDIAN_LITTLE)
+    return "little endian";
+  else
+    return "endianness unknown";
+}
+
 /* List the targets that BFD is configured to support, each followed
    by its endianness and the architectures it supports.  */
 
@@ -1698,8 +1717,8 @@ display_target_list ()
       int a;
 
       printf ("%s\n (header %s, data %s)\n", p->name,
-             p->header_byteorder_big_p ? "big endian" : "little endian",
-             p->byteorder_big_p ? "big endian" : "little endian");
+             endian_string (p->header_byteorder),
+             endian_string (p->byteorder));
 
       if (abfd == NULL)
        {
diff --git a/binutils/prdbg.c b/binutils/prdbg.c
new file mode 100644 (file)
index 0000000..c8c2bab
--- /dev/null
@@ -0,0 +1,1707 @@
+/* prdbg.c -- Print out generic 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 prints out the generic debugging information, by
+   supplying a set of routines to debug_write.  */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "bfd.h"
+#include "bucomm.h"
+#include "libiberty.h"
+#include "debug.h"
+#include "budbg.h"
+
+/* This is the structure we use as a handle for these routines.  */
+
+struct pr_handle
+{
+  /* File to print information to.  */
+  FILE *f;
+  /* Current indentation level.  */
+  unsigned int indent;
+  /* Type stack.  */
+  struct pr_stack *stack;
+  /* Parameter number we are about to output.  */
+  int parameter;
+};
+
+/* The type stack.  */
+
+struct pr_stack
+{
+  /* Next element on the stack.  */
+  struct pr_stack *next;
+  /* This element.  */
+  char *type;
+  /* Current visibility of fields if this is a class.  */
+  enum debug_visibility visibility;
+  /* Name of the current method we are handling.  */
+  const char *method;
+};
+
+static void indent PARAMS ((struct pr_handle *));
+static boolean push_type PARAMS ((struct pr_handle *, const char *));
+static boolean prepend_type PARAMS ((struct pr_handle *, const char *));
+static boolean append_type PARAMS ((struct pr_handle *, const char *));
+static boolean substitute_type PARAMS ((struct pr_handle *, const char *));
+static boolean indent_type PARAMS ((struct pr_handle *));
+static char *pop_type PARAMS ((struct pr_handle *));
+static void print_vma PARAMS ((bfd_vma, char *, boolean, boolean));
+static boolean pr_fix_visibility
+  PARAMS ((struct pr_handle *, enum debug_visibility));
+
+static boolean pr_start_compilation_unit PARAMS ((PTR, const char *));
+static boolean pr_start_source PARAMS ((PTR, const char *));
+static boolean pr_empty_type PARAMS ((PTR));
+static boolean pr_void_type PARAMS ((PTR));
+static boolean pr_int_type PARAMS ((PTR, unsigned int, boolean));
+static boolean pr_float_type PARAMS ((PTR, unsigned int));
+static boolean pr_complex_type PARAMS ((PTR, unsigned int));
+static boolean pr_bool_type PARAMS ((PTR, unsigned int));
+static boolean pr_enum_type PARAMS ((PTR, const char **, bfd_signed_vma *));
+static boolean pr_pointer_type PARAMS ((PTR));
+static boolean pr_function_type PARAMS ((PTR));
+static boolean pr_reference_type PARAMS ((PTR));
+static boolean pr_range_type PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma));
+static boolean pr_array_type
+  PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma, boolean));
+static boolean pr_set_type PARAMS ((PTR, boolean));
+static boolean pr_offset_type PARAMS ((PTR));
+static boolean pr_method_type PARAMS ((PTR, boolean, int));
+static boolean pr_const_type PARAMS ((PTR));
+static boolean pr_volatile_type PARAMS ((PTR));
+static boolean pr_start_struct_type PARAMS ((PTR, boolean, unsigned int));
+static boolean pr_struct_field
+  PARAMS ((PTR, const char *, bfd_vma, bfd_vma, enum debug_visibility));
+static boolean pr_end_struct_type PARAMS ((PTR));
+static boolean pr_start_class_type
+  PARAMS ((PTR, boolean, unsigned int, boolean, boolean));
+static boolean pr_class_static_member
+  PARAMS ((PTR, const char *, const char *, enum debug_visibility));
+static boolean pr_class_baseclass
+  PARAMS ((PTR, bfd_vma, boolean, enum debug_visibility));
+static boolean pr_class_start_method PARAMS ((PTR, const char *));
+static boolean pr_class_method_variant
+  PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean,
+          bfd_vma, boolean));
+static boolean pr_class_static_method_variant
+  PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean));
+static boolean pr_class_end_method PARAMS ((PTR));
+static boolean pr_end_class_type PARAMS ((PTR));
+static boolean pr_typedef_type PARAMS ((PTR, const char *));
+static boolean pr_tag_type PARAMS ((PTR, const char *, enum debug_type_kind));
+static boolean pr_typdef PARAMS ((PTR, const char *));
+static boolean pr_tag PARAMS ((PTR, const char *));
+static boolean pr_int_constant PARAMS ((PTR, const char *, bfd_vma));
+static boolean pr_float_constant PARAMS ((PTR, const char *, double));
+static boolean pr_typed_constant PARAMS ((PTR, const char *, bfd_vma));
+static boolean pr_variable
+  PARAMS ((PTR, const char *, enum debug_var_kind, bfd_vma));
+static boolean pr_start_function PARAMS ((PTR, const char *, boolean));
+static boolean pr_function_parameter
+  PARAMS ((PTR, const char *, enum debug_parm_kind, bfd_vma));
+static boolean pr_start_block PARAMS ((PTR, bfd_vma));
+static boolean pr_lineno PARAMS ((PTR, const char *, unsigned long, bfd_vma));
+static boolean pr_end_block PARAMS ((PTR, bfd_vma));
+static boolean pr_end_function PARAMS ((PTR));
+
+static const struct debug_write_fns pr_fns =
+{
+  pr_start_compilation_unit,
+  pr_start_source,
+  pr_empty_type,
+  pr_void_type,
+  pr_int_type,
+  pr_float_type,
+  pr_complex_type,
+  pr_bool_type,
+  pr_enum_type,
+  pr_pointer_type,
+  pr_function_type,
+  pr_reference_type,
+  pr_range_type,
+  pr_array_type,
+  pr_set_type,
+  pr_offset_type,
+  pr_method_type,
+  pr_const_type,
+  pr_volatile_type,
+  pr_start_struct_type,
+  pr_struct_field,
+  pr_end_struct_type,
+  pr_start_class_type,
+  pr_class_static_member,
+  pr_class_baseclass,
+  pr_class_start_method,
+  pr_class_method_variant,
+  pr_class_static_method_variant,
+  pr_class_end_method,
+  pr_end_class_type,
+  pr_typedef_type,
+  pr_tag_type,
+  pr_typdef,
+  pr_tag,
+  pr_int_constant,
+  pr_float_constant,
+  pr_typed_constant,
+  pr_variable,
+  pr_start_function,
+  pr_function_parameter,
+  pr_start_block,
+  pr_lineno,
+  pr_end_block,
+  pr_end_function
+};
+\f
+/* Print out the generic debugging information recorded in dhandle.  */
+
+boolean
+print_debugging_info (f, dhandle)
+     FILE *f;
+     PTR dhandle;
+{
+  struct pr_handle info;
+
+  info.f = f;
+  info.indent = 0;
+  info.stack = NULL;
+  info.parameter = 0;
+
+  return debug_write (dhandle, &pr_fns, (PTR) &info);
+}
+\f
+/* Indent to the current indentation level.  */
+
+static void
+indent (info)
+     struct pr_handle *info;
+{
+  unsigned int i;
+
+  for (i = 0; i < info->indent; i++)
+    putc (' ', info->f);
+}
+
+/* Push a type on the type stack.  */
+
+static boolean
+push_type (info, type)
+     struct pr_handle *info;
+     const char *type;
+{
+  struct pr_stack *n;
+
+  if (type == NULL)
+    return false;
+
+  n = (struct pr_stack *) xmalloc (sizeof *n);
+  memset (n, 0, sizeof *n);
+
+  n->type = xstrdup (type);
+  n->visibility = DEBUG_VISIBILITY_IGNORE;
+  n->method = NULL;
+  n->next = info->stack;
+  info->stack = n;
+
+  return true;
+}
+
+/* Prepend a string onto the type on the top of the type stack.  */
+
+static boolean
+prepend_type (info, s)
+     struct pr_handle *info;
+     const char *s;
+{
+  char *n;
+
+  assert (info->stack != NULL);
+
+  n = (char *) xmalloc (strlen (s) + strlen (info->stack->type) + 1);
+  sprintf (n, "%s%s", s, info->stack->type);
+  free (info->stack->type);
+  info->stack->type = n;
+
+  return true;
+}
+
+/* Append a string to the type on the top of the type stack.  */
+
+static boolean
+append_type (info, s)
+     struct pr_handle *info;
+     const char *s;
+{
+  unsigned int len;
+
+  if (s == NULL)
+    return false;
+
+  assert (info->stack != NULL);
+
+  len = strlen (info->stack->type);
+  info->stack->type = (char *) xrealloc (info->stack->type,
+                                        len + strlen (s) + 1);
+  strcpy (info->stack->type + len, s);
+
+  return true;
+}
+
+/* We use an underscore to indicate where the name should go in a type
+   string.  This function substitutes a string for the underscore.  If
+   there is no underscore, the name follows the type.  */
+
+static boolean
+substitute_type (info, s)
+     struct pr_handle *info;
+     const char *s;
+{
+  char *u;
+
+  assert (info->stack != NULL);
+
+  u = strchr (info->stack->type, '|');
+  if (u != NULL)
+    {
+      char *n;
+
+      n = (char *) xmalloc (strlen (info->stack->type) + strlen (s));
+
+      memcpy (n, info->stack->type, u - info->stack->type);
+      strcpy (n + (u - info->stack->type), s);
+      strcat (n, u + 1);
+
+      free (info->stack->type);
+      info->stack->type = n;
+
+      return true;
+    }
+
+  if (strchr (s, '|') != NULL
+      && (strchr (info->stack->type, '{') != NULL
+         || strchr (info->stack->type, '(') != NULL))
+    {
+      if (! prepend_type (info, "(")
+         || ! append_type (info, ")"))
+       return false;
+    }
+
+  if (*s == '\0')
+    return true;
+
+  return (append_type (info, " ")
+         && append_type (info, s));
+}
+
+/* Indent the type at the top of the stack by appending spaces.  */
+
+static boolean
+indent_type (info)
+     struct pr_handle *info;
+{
+  unsigned int i;
+
+  for (i = 0; i < info->indent; i++)
+    {
+      if (! append_type (info, " "))
+       return false;
+    }
+
+  return true;
+}
+
+/* Pop a type from the type stack.  */
+
+static char *
+pop_type (info)
+     struct pr_handle *info;
+{
+  struct pr_stack *o;
+  char *ret, *s;
+
+  assert (info->stack != NULL);
+
+  o = info->stack;
+  info->stack = o->next;
+  ret = o->type;
+  free (o);
+
+  s = strchr (ret, '+');
+  if (s != NULL)
+    memmove (s, s + 2, strlen (s + 2) + 1);
+
+  return ret;
+}
+
+/* Print a VMA value into a string.  */
+
+static void
+print_vma (vma, buf, unsignedp, hexp)
+     bfd_vma vma;
+     char *buf;
+     boolean unsignedp;
+     boolean hexp;
+{
+  if (sizeof (vma) <= sizeof (unsigned long))
+    {
+      if (hexp)
+       sprintf (buf, "0x%lx", (unsigned long) vma);
+      else if (unsignedp)
+       sprintf (buf, "%lu", (unsigned long) vma);
+      else
+       sprintf (buf, "%ld", (long) vma);
+    }
+  else
+    {
+      buf[0] = '0';
+      buf[1] = 'x';
+      sprintf_vma (buf + 2, vma);
+    }
+}
+\f
+/* Start a new compilation unit.  */
+
+static boolean
+pr_start_compilation_unit (p, filename)
+     PTR p;
+     const char *filename;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  assert (info->indent == 0);
+
+  fprintf (info->f, "%s:\n", filename);
+
+  return true;
+}
+
+/* Start a source file within a compilation unit.  */
+
+static boolean
+pr_start_source (p, filename)
+     PTR p;
+     const char *filename;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  assert (info->indent == 0);
+
+  fprintf (info->f, " %s:\n", filename);
+
+  return true;
+}
+
+/* Push an empty type onto the type stack.  */
+
+static boolean
+pr_empty_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  return push_type (info, "<undefined>");
+}
+
+/* Push a void type onto the type stack.  */
+
+static boolean
+pr_void_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  return push_type (info, "void");
+}
+
+/* Push an integer type onto the type stack.  */
+
+static boolean
+pr_int_type (p, size, unsignedp)
+     PTR p;
+     unsigned int size;
+     boolean unsignedp;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[10];
+
+  sprintf (ab, "%sint%d", unsignedp ? "u" : "", size * 8);
+  return push_type (info, ab);
+}
+
+/* Push a floating type onto the type stack.  */
+
+static boolean
+pr_float_type (p, size)
+     PTR p;
+     unsigned int size;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[10];
+
+  if (size == 4)
+    return push_type (info, "float");
+  else if (size == 8)
+    return push_type (info, "double");
+
+  sprintf (ab, "float%d", size * 8);
+  return push_type (info, ab);
+}
+
+/* Push a complex type onto the type stack.  */
+
+static boolean
+pr_complex_type (p, size)
+     PTR p;
+     unsigned int size;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  if (! pr_float_type (p, size))
+    return false;
+
+  return prepend_type (info, "complex ");
+}
+
+/* Push a boolean type onto the type stack.  */
+
+static boolean
+pr_bool_type (p, size)
+     PTR p;
+     unsigned int size;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[10];
+
+  sprintf (ab, "bool%d", size * 8);
+
+  return push_type (info, ab);
+}
+
+/* Push an enum type onto the type stack.  */
+
+static boolean
+pr_enum_type (p, names, values)
+     PTR p;
+     const char **names;
+     bfd_signed_vma *values;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  unsigned int i;
+  bfd_signed_vma val;
+
+  /* The + indicates where the tag goes, if there is one.  */
+  if (! push_type (info, "enum + { "))
+    return false;
+
+  val = 0;
+  for (i = 0; names[i] != NULL; i++)
+    {
+      if (i > 0)
+       {
+         if (! append_type (info, ", "))
+           return false;
+       }
+
+      if (! append_type (info, names[i]))
+       return false;
+
+      if (values[i] != val)
+       {
+         char ab[20];
+
+         print_vma (values[i], ab, false, false);
+         if (! append_type (info, " = ")
+             || ! append_type (info, ab))
+           return false;
+         val = values[i];
+       }
+
+      ++val;
+    }
+
+  return append_type (info, " }");
+}
+
+/* Turn the top type on the stack into a pointer.  */
+
+static boolean
+pr_pointer_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  assert (info->stack != NULL);
+
+  return substitute_type (info, "*|");
+}
+
+/* Turn the top type on the stack into a function returning that type.  */
+
+static boolean
+pr_function_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  assert (info->stack != NULL);
+
+  return substitute_type (info, "(|) ()");
+}
+
+/* Turn the top type on the stack into a reference to that type.  */
+
+static boolean
+pr_reference_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  assert (info->stack != NULL);
+
+  return substitute_type (info, "&|");
+}
+
+/* Make a range type.  */
+
+static boolean
+pr_range_type (p, lower, upper)
+     PTR p;
+     bfd_signed_vma lower;
+     bfd_signed_vma upper;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char abl[20], abu[20];
+
+  assert (info->stack != NULL);
+
+  if (! substitute_type (info, ""))
+    return false;
+
+  print_vma (lower, abl, false, false);
+  print_vma (upper, abu, false, false);
+
+  return (prepend_type (info, "range (")
+         && append_type (info, "):")
+         && append_type (info, abl)
+         && append_type (info, ":")
+         && append_type (info, abu));
+}
+
+/* Make an array type.  */
+
+/*ARGSUSED*/
+static boolean
+pr_array_type (p, lower, upper, stringp)
+     PTR p;
+     bfd_signed_vma lower;
+     bfd_signed_vma upper;
+     boolean stringp;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *range_type;
+  char abl[20], abu[20], ab[50];
+
+  range_type = pop_type (info);
+  if (range_type == NULL)
+    return false;
+
+  if (lower == 0)
+    {
+      if (upper == -1)
+       sprintf (ab, "|[]");
+      else
+       {
+         print_vma (upper + 1, abu, false, false);
+         sprintf (ab, "|[%s]", abu);
+       }
+    }
+  else
+    {
+      print_vma (lower, abl, false, false);
+      print_vma (upper, abu, false, false);
+      sprintf (ab, "|[%s:%s]", abl, abu);
+    }
+
+  if (! substitute_type (info, ab))
+    return false;
+
+  if (strcmp (range_type, "int") != 0)
+    {
+      if (! append_type (info, ":")
+         || ! append_type (info, range_type))
+       return false;
+    }
+
+  if (stringp)
+    {
+      if (! append_type (info, " /* string */"))
+       return false;
+    }
+
+  return true;
+}
+
+/* Make a set type.  */
+
+/*ARGSUSED*/
+static boolean
+pr_set_type (p, bitstringp)
+     PTR p;
+     boolean bitstringp;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  if (! substitute_type (info, ""))
+    return false;
+
+  if (! prepend_type (info, "set { ")
+      || ! append_type (info, " }"))
+    return false;
+
+  if (bitstringp)
+    {
+      if (! append_type (info, "/* bitstring */"))
+       return false;
+    }
+
+  return true;
+}
+
+/* Make an offset type.  */
+
+static boolean
+pr_offset_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+
+  if (! substitute_type (info, ""))
+    return false;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  return (substitute_type (info, "")
+         && prepend_type (info, " ")
+         && prepend_type (info, t)
+         && append_type (info, "::|"));
+}
+
+/* Make a method type.  */
+
+static boolean
+pr_method_type (p, domain, argcount)
+     PTR p;
+     boolean domain;
+     int argcount;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  unsigned int len;
+  char *domain_type;
+  char **arg_types;
+  char *s;
+
+  len = 10;
+
+  if (! domain)
+    domain_type = NULL;
+  else
+    {
+      if (! substitute_type (info, ""))
+       return false;
+      domain_type = pop_type (info);
+      if (domain_type == NULL)
+       return false;
+      if (strncmp (domain_type, "class ", sizeof "class " - 1) == 0
+         && strchr (domain_type + sizeof "class " - 1, ' ') == NULL)
+       domain_type += sizeof "class " - 1;
+      else if (strncmp (domain_type, "union class ",
+                       sizeof "union class ") == 0
+              && (strchr (domain_type + sizeof "union class " - 1, ' ')
+                  == NULL))
+       domain_type += sizeof "union class " - 1;
+      len += strlen (domain_type);
+    }
+
+  if (argcount <= 0)
+    {
+      arg_types = NULL;
+      len += 15;
+    }
+  else
+    {
+      int i;
+
+      arg_types = (char **) xmalloc (argcount * sizeof *arg_types);
+      for (i = argcount - 1; i >= 0; i--)
+       {
+         if (! substitute_type (info, ""))
+           return false;
+         arg_types[i] = pop_type (info);
+         if (arg_types[i] == NULL)
+           return false;
+         len += strlen (arg_types[i]) + 2;
+       }
+    }
+
+  /* Now the return type is on the top of the stack.  */
+
+  s = (char *) xmalloc (len);
+  if (! domain)
+    *s = '\0';
+  else
+    strcpy (s, domain_type);
+  strcat (s, "::| (");
+
+  if (argcount < 0)
+    strcat (s, "/* unknown */");
+  else
+    {
+      int i;
+
+      for (i = 0; i < argcount; i++)
+       {
+         if (i > 0)
+           strcat (s, ", ");
+         strcat (s, arg_types[i]);
+       }
+    }
+
+  strcat (s, ")");
+
+  if (! substitute_type (info, s))
+    return false;
+
+  free (s);
+
+  return true;
+}
+
+/* Make a const qualified type.  */
+
+static boolean
+pr_const_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  return substitute_type (info, "const |");
+}
+
+/* Make a volatile qualified type.  */
+
+static boolean
+pr_volatile_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  return substitute_type (info, "volatile |");
+}
+
+/* Start accumulating a struct type.  */
+
+static boolean
+pr_start_struct_type (p, structp, size)
+     PTR p;
+     boolean structp;
+     unsigned int size;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  const char *t;
+  char ab[30];
+
+  info->indent += 2;
+
+  if (structp)
+    t = "struct";
+  else
+    t = "union";
+  if (size != 0)
+    sprintf (ab, "%s + { /* size %u */\n", t, size);
+  else
+    sprintf (ab, "%s + {\n", t);
+  if (! push_type (info, ab))
+    return false;
+  info->stack->visibility = DEBUG_VISIBILITY_PUBLIC;
+  return indent_type (info);
+}
+
+/* Output the visibility of a field in a struct.  */
+
+static boolean
+pr_fix_visibility (info, visibility)
+     struct pr_handle *info;
+     enum debug_visibility visibility;
+{
+  const char *s;
+  struct pr_stack *top;
+  char *t;
+  unsigned int len;
+
+  assert (info->stack != NULL && info->stack->next != NULL);
+
+  if (info->stack->next->visibility == visibility)
+    return true;
+
+  assert (info->stack->next->visibility != DEBUG_VISIBILITY_IGNORE);
+
+  switch (visibility)
+    {
+    case DEBUG_VISIBILITY_PUBLIC:
+      s = "public";
+      break;
+    case DEBUG_VISIBILITY_PRIVATE:
+      s = "private";
+      break;
+    case DEBUG_VISIBILITY_PROTECTED:
+      s = "protected";
+      break;
+    default:
+      abort ();
+      return false;
+    }
+
+  /* Trim off a trailing space in the struct string, to make the
+     output look a bit better, then stick on the visibility string.
+     Pop the stack temporarily to make the string manipulation
+     simpler.  */
+
+  top = info->stack;
+  info->stack = top->next;
+
+  t = info->stack->type;
+  len = strlen (t);
+  assert (t[len - 1] == ' ');
+  t[len - 1] = '\0';
+
+  if (! append_type (info, s)
+      || ! append_type (info, ":\n")
+      || ! indent_type (info))
+    return false;
+
+  info->stack->visibility = visibility;
+
+  info->stack = top;
+
+  return true;
+}
+
+/* Add a field to a struct type.  */
+
+static boolean
+pr_struct_field (p, name, bitpos, bitsize, visibility)
+     PTR p;
+     const char *name;
+     bfd_vma bitpos;
+     bfd_vma bitsize;
+     enum debug_visibility visibility;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[20];
+
+  if (! pr_fix_visibility (info, visibility))
+    return false;
+
+  if (! substitute_type (info, name))
+    return false;
+
+  if (! append_type (info, "; /* "))
+    return false;
+
+  if (bitsize != 0)
+    {
+      print_vma (bitsize, ab, true, false);
+      if (! append_type (info, "bitsize ")
+         || ! append_type (info, ab)
+         || ! append_type (info, ", "))
+       return false;
+    }
+
+  print_vma (bitpos, ab, true, false);
+  if (! append_type (info, "bitpos ")
+      || ! append_type (info, ab)
+      || ! append_type (info, " */\n")
+      || ! indent_type (info))
+    return false;
+
+  return append_type (info, pop_type (info));
+}
+
+/* Finish a struct type.  */
+
+static boolean
+pr_end_struct_type (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *s;
+
+  assert (info->stack != NULL);
+  assert (info->indent >= 2);
+
+  info->indent -= 2;
+
+  /* Change the trailing indentation to have a close brace.  */
+  s = info->stack->type + strlen (info->stack->type) - 2;
+  assert (strcmp (s, "  ") == 0);
+
+  *s++ = '}';
+  *s = '\0';
+
+  return true;
+}
+
+/* Start a class type.  */
+
+static boolean
+pr_start_class_type (p, structp, size, vptr, ownvptr)
+     PTR p;
+     boolean structp;
+     unsigned int size;
+     boolean vptr;
+     boolean ownvptr;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *tv = NULL;
+
+  info->indent += 2;
+
+  if (vptr && ! ownvptr)
+    {
+      tv = pop_type (info);
+      if (tv == NULL)
+       return false;
+    }
+
+  if (! push_type (info, structp ? "class" : "union class")
+      || ! append_type (info, " + {"))
+    return false;
+  if (size != 0 || vptr || ownvptr)
+    {
+      if (! append_type (info, " /*"))
+       return false;
+
+      if (size != 0)
+       {
+         char ab[20];
+
+         sprintf (ab, "%u", size);
+         if (! append_type (info, " size ")
+             || ! append_type (info, ab))
+           return false;
+       }
+
+      if (vptr)
+       {
+         if (! append_type (info, " vtable "))
+           return false;
+         if (ownvptr)
+           {
+             if (! append_type (info, "self "))
+               return false;
+           }
+         else
+           {
+             if (! append_type (info, tv)
+                 || ! append_type (info, " "))
+               return false;
+           }
+       }
+
+      if (! append_type (info, " */"))
+       return false;
+    }
+
+  info->stack->visibility = DEBUG_VISIBILITY_PRIVATE;
+
+  return (append_type (info, "\n")
+         && indent_type (info));
+}
+
+/* Add a static member to a class.  */
+
+static boolean
+pr_class_static_member (p, name, physname, visibility)
+     PTR p;
+     const char *name;
+     const char *physname;
+     enum debug_visibility visibility;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  if (! pr_fix_visibility (info, visibility))
+    return false;
+
+  if (! substitute_type (info, name))
+    return false;
+
+  return (prepend_type (info, "static ")
+         && append_type (info, "; /* physname ")
+         && append_type (info, physname)
+         && append_type (info, " */\n")
+         && indent_type (info)
+         && append_type (info, pop_type (info)));
+}
+
+/* Add a base class to a class.  */
+
+static boolean
+pr_class_baseclass (p, bitpos, virtual, visibility)
+     PTR p;
+     bfd_vma bitpos;
+     boolean virtual;
+     enum debug_visibility visibility;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  const char *prefix;
+  char ab[20];
+  char *s, *n;
+
+  assert (info->stack != NULL && info->stack->next != NULL);
+
+  if (! substitute_type (info, ""))
+    return false;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  if (strncmp (t, "class ", sizeof "class " - 1) == 0)
+    t += sizeof "class " - 1;
+
+  /* Push it back on to take advantage of the prepend_type and
+     append_type routines.  */
+  if (! push_type (info, t))
+    return false;
+
+  if (virtual)
+    {
+      if (! prepend_type (info, "virtual "))
+       return false;
+    }
+
+  switch (visibility)
+    {
+    case DEBUG_VISIBILITY_PUBLIC:
+      prefix = "public ";
+      break;
+    case DEBUG_VISIBILITY_PROTECTED:
+      prefix = "protected ";
+      break;
+    case DEBUG_VISIBILITY_PRIVATE:
+      prefix = "private ";
+      break;
+    default:
+      prefix = "/* unknown visibility */ ";
+      break;
+    }
+
+  if (! prepend_type (info, prefix))
+    return false;
+
+  if (bitpos != 0)
+    {
+      print_vma (bitpos, ab, true, false);
+      if (! append_type (info, " /* bitpos ")
+         || ! append_type (info, ab)
+         || ! append_type (info, " */"))
+       return false;
+    }
+
+  /* Now the top of the stack is something like "public A / * bitpos
+     10 * /".  The next element on the stack is something like "class
+     + { / * size 8 * /\n...".  We want to substitute the top of the
+     stack in after the +.  */
+  s = strchr (info->stack->next->type, '+');
+  assert (s != NULL);
+
+  if (s[2] != ':')
+    {
+      ++s;
+      assert (s[1] == '{');
+      if (! prepend_type (info, " : "))
+       return false;
+    }
+  else
+    {
+      /* We already have a baseclass.  Append this one after a comma.  */
+      s = strchr (s, '{');
+      assert (s != NULL);
+      --s;
+      if (! prepend_type (info, ", "))
+       return false;
+    }
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  n = (char *) xmalloc (strlen (info->stack->type) + strlen (t) + 1);
+  memcpy (n, info->stack->type, s - info->stack->type);
+  strcpy (n + (s - info->stack->type), t);
+  strcat (n, s);
+
+  free (info->stack->type);
+  info->stack->type = n;
+
+  free (t);
+
+  return true;
+}
+
+/* Start adding a method to a class.  */
+
+static boolean
+pr_class_start_method (p, name)
+     PTR p;
+     const char *name;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  if (! push_type (info, ""))
+    return false;
+  info->stack->method = name;
+  return true;
+}
+
+/* Add a variant to a method.  */
+
+static boolean
+pr_class_method_variant (p, argtypes, visibility, constp, volatilep, voffset,
+                        context)
+     PTR p;
+     const char *argtypes;
+     enum debug_visibility visibility;
+     boolean constp;
+     boolean volatilep;
+     bfd_vma voffset;
+     boolean context;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *method_type;
+  char *context_type;
+
+  assert (info->stack != NULL);
+  assert (info->stack->next != NULL);
+
+  /* Put the const and volatile qualifiers on the type.  */
+  if (volatilep)
+    {
+      if (! prepend_type (info, "volatile "))
+       return false;
+    }
+  if (constp)
+    {
+      if (! prepend_type (info, "const "))
+       return false;
+    }
+
+  /* Stick the name of the method into its type.  */
+  if (! substitute_type (info,
+                        (context
+                         ? info->stack->next->next->method
+                         : info->stack->next->method)))
+    return false;
+
+  /* Get the type.  */
+  method_type = pop_type (info);
+  if (method_type == NULL)
+    return false;
+
+  /* Pull off the context type if there is one.  */
+  if (! context)
+    context_type = NULL;
+  else
+    {
+      context_type = pop_type (info);
+      if (context_type == NULL)
+       return false;
+    }
+
+  /* Now the top of the stack is the holder for the method, and the
+     second element on the stack is the class.  */
+
+  if (! pr_fix_visibility (info, visibility))
+    return false;
+
+  if (! append_type (info, method_type)
+      || ! append_type (info, " /* ")
+      || ! append_type (info, argtypes))
+    return false;
+  if (context || voffset != 0)
+    {
+      char ab[20];
+
+      if (context)
+       {
+         if (! append_type (info, "context ")
+             || ! append_type (info, context_type)
+             || ! append_type (info, " "))
+           return false;
+       }
+      print_vma (voffset, ab, true, false);
+      if (! append_type (info, "voffset ")
+         || ! append_type (info, ab))
+       return false;
+    }
+
+  return (append_type (info, " */;\n")
+         && indent_type (info));
+}
+
+/* Add a static variant to a method.  */
+
+static boolean
+pr_class_static_method_variant (p, argtypes, visibility, constp, volatilep)
+     PTR p;
+     const char *argtypes;
+     enum debug_visibility visibility;
+     boolean constp;
+     boolean volatilep;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *method_type;
+
+  assert (info->stack != NULL);
+  assert (info->stack->next != NULL);
+  assert (info->stack->next->method != NULL);
+
+  /* Put the const and volatile qualifiers on the type.  */
+  if (volatilep)
+    {
+      if (! prepend_type (info, "volatile "))
+       return false;
+    }
+  if (constp)
+    {
+      if (! prepend_type (info, "const "))
+       return false;
+    }
+
+  /* Mark it as static.  */
+  if (! prepend_type (info, "static "))
+    return false;
+
+  /* Stick the name of the method into its type.  */
+  if (! substitute_type (info, info->stack->next->method))
+    return false;
+
+  /* Get the type.  */
+  method_type = pop_type (info);
+  if (method_type == NULL)
+    return false;
+
+  /* Now the top of the stack is the holder for the method, and the
+     second element on the stack is the class.  */
+
+  if (! pr_fix_visibility (info, visibility))
+    return false;
+
+  return (append_type (info, method_type)
+         && append_type (info, " /* ")
+         && append_type (info, argtypes)
+         && append_type (info, " */;\n")
+         && indent_type (info));
+}
+
+/* Finish up a method.  */
+
+static boolean
+pr_class_end_method (p)
+     PTR p;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  /* The method variants have been appended to the string on top of
+     the stack with the correct indentation.  We just need the append
+     the string on top of the stack to the class string that is second
+     on the stack.  */
+  return append_type (info, pop_type (info));
+}
+
+/* Finish up a class.  */
+
+static boolean
+pr_end_class_type (p)
+     PTR p;
+{
+  return pr_end_struct_type (p);
+}
+
+/* Push a type on the stack using a typedef name.  */
+
+static boolean
+pr_typedef_type (p, name)
+     PTR p;
+     const char *name;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  return push_type (info, name);
+}
+
+/* Push a type on the stack using a tag name.  */
+
+static boolean
+pr_tag_type (p, name, kind)
+     PTR p;
+     const char *name;
+     enum debug_type_kind kind;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  const char *t;
+
+  switch (kind)
+    {
+    case DEBUG_KIND_STRUCT:
+      t = "struct ";
+      break;
+    case DEBUG_KIND_UNION:
+      t = "union ";
+      break;
+    case DEBUG_KIND_ENUM:
+      t = "enum ";
+      break;
+    case DEBUG_KIND_CLASS:
+      t = "class ";
+      break;
+    case DEBUG_KIND_UNION_CLASS:
+      t = "union class ";
+      break;
+    default:
+      abort ();
+      return false;
+    }
+
+  return (push_type (info, t)
+         && append_type (info, name));
+}
+
+/* Output a typedef.  */
+
+static boolean
+pr_typdef (p, name)
+     PTR p;
+     const char *name;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *s;
+
+  if (! substitute_type (info, name))
+    return false;
+
+  s = pop_type (info);
+  if (s == NULL)
+    return false;
+
+  indent (info);
+  fprintf (info->f, "typedef %s;\n", s);
+
+  free (s);
+
+  return true;
+}
+
+/* Output a tag.  */
+
+static boolean
+pr_tag (p, name)
+     PTR p;
+     const char *name;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t, *s, *n;
+
+  assert (info->stack != NULL);
+
+  t = info->stack->type;
+
+  s = strchr (t, '+');
+  assert (s != NULL);
+
+  n = (char *) xmalloc (strlen (t) + strlen (name));
+
+  memcpy (n, t, s - t);
+  strcpy (n + (s - t), name);
+  strcat (n, s + 1);
+
+  free (t);
+  info->stack->type = n;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  indent (info);
+  fprintf (info->f, "%s;\n", t);
+
+  free (n);
+
+  return true;
+}
+
+/* Output an integer constant.  */
+
+static boolean
+pr_int_constant (p, name, val)
+     PTR p;
+     const char *name;
+     bfd_vma val;
+{
+  struct pr_handle *info = (struct pr_handle *) info;
+  char ab[20];
+
+  indent (info);
+  print_vma (val, ab, false, false);
+  fprintf (info->f, "const int %s = %s;\n", name, ab);
+  return true;
+}
+
+/* Output a floating point constant.  */
+
+static boolean
+pr_float_constant (p, name, val)
+     PTR p;
+     const char *name;
+     double val;
+{
+  struct pr_handle *info = (struct pr_handle *) info;
+
+  indent (info);
+  fprintf (info->f, "const double %s = %g;\n", name, val);
+  return true;
+}
+
+/* Output a typed constant.  */
+
+static boolean
+pr_typed_constant (p, name, val)
+     PTR p;
+     const char *name;
+     bfd_vma val;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  char ab[20];
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  indent (info);
+  print_vma (val, ab, false, false);
+  fprintf (info->f, "const %s %s = %s;\n", t, name, ab);
+
+  free (t);
+
+  return true;
+}
+
+/* Output a variable.  */
+
+static boolean
+pr_variable (p, name, kind, val)
+     PTR p;
+     const char *name;
+     enum debug_var_kind kind;
+     bfd_vma val;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  char ab[20];
+
+  if (! substitute_type (info, name))
+    return false;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  indent (info);
+  switch (kind)
+    {
+    case DEBUG_STATIC:
+    case DEBUG_LOCAL_STATIC:
+      fprintf (info->f, "static ");
+      break;
+    case DEBUG_REGISTER:
+      fprintf (info->f, "register ");
+      break;
+    default:
+      break;
+    }
+  print_vma (val, ab, true, true);
+  fprintf (info->f, "%s /* %s */;\n", t, ab);
+
+  free (t);
+
+  return true;
+}
+
+/* Start outputting a function.  */
+
+static boolean
+pr_start_function (p, name, global)
+     PTR p;
+     const char *name;
+     boolean global;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+
+  if (! substitute_type (info, name))
+    return false;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  indent (info);
+  if (! global)
+    fprintf (info->f, "static ");
+  fprintf (info->f, "%s (", t);
+
+  info->parameter = 1;
+
+  return true;
+}
+
+/* Output a function parameter.  */
+
+static boolean
+pr_function_parameter (p, name, kind, val)
+     PTR p;
+     const char *name;
+     enum debug_parm_kind kind;
+     bfd_vma val;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  char ab[20];
+
+  if (kind == DEBUG_PARM_REFERENCE
+      || kind == DEBUG_PARM_REF_REG)
+    {
+      if (! pr_reference_type (p))
+       return false;
+    }
+
+  if (! substitute_type (info, name))
+    return false;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return false;
+
+  if (info->parameter != 1)
+    fprintf (info->f, ", ");
+
+  if (kind == DEBUG_PARM_REG || kind == DEBUG_PARM_REF_REG)
+    fprintf (info->f, "register ");
+
+  print_vma (val, ab, true, true);
+  fprintf (info->f, "%s /* %s */", t, ab);
+
+  free (t);
+
+  ++info->parameter;
+
+  return true;
+}
+
+/* Start writing out a block.  */
+
+static boolean
+pr_start_block (p, addr)
+     PTR p;
+     bfd_vma addr;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[20];
+
+  if (info->parameter > 0)
+    {
+      fprintf (info->f, ")\n");
+      info->parameter = 0;
+    }
+
+  indent (info);
+  print_vma (addr, ab, true, true);
+  fprintf (info->f, "{ /* %s */\n", ab);
+
+  info->indent += 2;
+
+  return true;
+}
+
+/* Write out line number information.  */
+
+static boolean
+pr_lineno (p, filename, lineno, addr)
+     PTR p;
+     const char *filename;
+     unsigned long lineno;
+     bfd_vma addr;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[20];
+
+  indent (info);
+  print_vma (addr, ab, true, true);
+  fprintf (info->f, "/* file %s line %lu addr %s */\n", filename, lineno, ab);
+
+  return true;
+}
+
+/* Finish writing out a block.  */
+
+static boolean
+pr_end_block (p, addr)
+     PTR p;
+     bfd_vma addr;
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[20];
+
+  info->indent -= 2;
+
+  indent (info);
+  print_vma (addr, ab, true, true);
+  fprintf (info->f, "} /* %s */\n", ab);
+
+  return true;
+}
+
+/* Finish writing out a function.  */
+
+/*ARGSUSED*/
+static boolean
+pr_end_function (p)
+     PTR p;
+{
+  return true;
+}
diff --git a/binutils/rddbg.c b/binutils/rddbg.c
new file mode 100644 (file)
index 0000000..8559621
--- /dev/null
@@ -0,0 +1,195 @@
+/* rddbg.c -- Read debugging information into a generic form.
+   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 reads debugging information into a generic form.  This
+   file knows how to dig the debugging information out of an object
+   file.  */
+
+#include "bfd.h"
+#include "bucomm.h"
+#include "libiberty.h"
+#include "debug.h"
+#include "budbg.h"
+
+static boolean read_section_stabs_debugging_info
+  PARAMS ((bfd *, PTR, boolean *));
+
+/* Read debugging information from a BFD.  Returns a generic debugging
+   pointer.  */
+
+PTR
+read_debugging_info (abfd)
+     bfd *abfd;
+{
+  PTR dhandle;
+  boolean found;
+
+  dhandle = debug_init ();
+  if (dhandle == NULL)
+    return NULL;
+
+  /* All we know about right now is stabs in sections.  */
+
+  if (! read_section_stabs_debugging_info (abfd, dhandle, &found))
+    return NULL;
+
+  if (! found)
+    {
+      fprintf (stderr, "%s: no recognized debugging information\n",
+              bfd_get_filename (abfd));
+      return NULL;
+    }
+
+  return dhandle;
+}
+
+/* Read stabs in sections debugging information from a BFD.  */
+
+static boolean
+read_section_stabs_debugging_info (abfd, dhandle, pfound)
+     bfd *abfd;
+     PTR dhandle;
+     boolean *pfound;
+{
+  static struct
+    {
+      const char *secname;
+      const char *strsecname;
+    } names[] = { { ".stab", ".stabstr" } };
+  unsigned int i;
+  PTR shandle;
+
+  *pfound = false;
+  shandle = NULL;
+
+  for (i = 0; i < sizeof names / sizeof names[0]; i++)
+    {
+      asection *sec, *strsec;
+
+      sec = bfd_get_section_by_name (abfd, names[i].secname);
+      strsec = bfd_get_section_by_name (abfd, names[i].strsecname);
+      if (sec != NULL && strsec != NULL)
+       {
+         bfd_size_type stabsize, strsize;
+         bfd_byte *stabs, *strings;
+         bfd_byte *stab;
+         bfd_size_type stroff, next_stroff;
+
+         stabsize = bfd_section_size (abfd, sec);
+         stabs = (bfd_byte *) xmalloc (stabsize);
+         if (! bfd_get_section_contents (abfd, sec, stabs, 0, stabsize))
+           {
+             fprintf (stderr, "%s: %s: %s\n",
+                      bfd_get_filename (abfd), names[i].secname,
+                      bfd_errmsg (bfd_get_error ()));
+             return false;
+           }
+
+         strsize = bfd_section_size (abfd, strsec);
+         strings = (bfd_byte *) xmalloc (strsize);
+         if (! bfd_get_section_contents (abfd, strsec, strings, 0, strsize))
+           {
+             fprintf (stderr, "%s: %s: %s\n",
+                      bfd_get_filename (abfd), names[i].strsecname,
+                      bfd_errmsg (bfd_get_error ()));
+             return false;
+           }
+
+         if (shandle == NULL)
+           {
+             shandle = start_stab (dhandle);
+             if (shandle == NULL)
+               return false;
+           }
+
+         *pfound = true;
+
+         stroff = 0;
+         next_stroff = 0;
+         for (stab = stabs; stab < stabs + stabsize; stab += 12)
+           {
+             bfd_size_type strx;
+             int type;
+             int other;
+             int desc;
+             bfd_vma value;
+
+             /* This code presumes 32 bit values.  */
+
+             strx = bfd_get_32 (abfd, stab);
+             type = bfd_get_8 (abfd, stab + 4);
+             other = bfd_get_8 (abfd, stab + 5);
+             desc = bfd_get_16 (abfd, stab + 6);
+             value = bfd_get_32 (abfd, stab + 8);
+
+             if (type == 0)
+               {
+                 /* Special type 0 stabs indicate the offset to the
+                     next string table.  */
+                 stroff = next_stroff;
+                 next_stroff += value;
+               }
+             else
+               {
+                 char *f, *s;
+
+                 f = NULL;
+                 s = (char *) strings + stroff + strx;
+                 while (s[strlen (s) - 1] == '\\'
+                        && stab + 12 < stabs + stabsize)
+                   {
+                     stab += 12;
+                     s[strlen (s) - 1] = '\0';
+                     s = concat (s,
+                                 ((char *) strings
+                                  + stroff
+                                  + bfd_get_32 (abfd, stab)),
+                                 (const char *) NULL);
+                     if (f != NULL)
+                       free (f);
+                     f = s;
+                   }
+
+                 if (! parse_stab (dhandle, shandle, type, desc, value, s))
+                   return false;
+
+                 /* Don't free f, since I think the stabs code
+                     expects strings to hang around.  This should be
+                     straightened out.  FIXME.  */
+               }
+           }
+
+         free (stabs);
+
+         /* Don't free strings, since I think the stabs code expects
+             the strings to hang around.  This should be straightened
+             out.  FIXME.  */
+       }
+    }
+
+  if (shandle != NULL)
+    {
+      if (! finish_stab (dhandle, shandle))
+       return false;
+    }
+
+  return true;
+}
diff --git a/binutils/stabs.c b/binutils/stabs.c
new file mode 100644 (file)
index 0000000..3d34301
--- /dev/null
@@ -0,0 +1,3174 @@
+/* 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;
+}