* objdump.c: New command line option --debugging-tags.
authorNick Clifton <nickc@redhat.com>
Tue, 22 Jul 2003 13:33:32 +0000 (13:33 +0000)
committerNick Clifton <nickc@redhat.com>
Tue, 22 Jul 2003 13:33:32 +0000 (13:33 +0000)
* doc/binutils.texi: Document new command line option.
* prdbg.c: Code to print the debug info as tags compatible with ctags.
* budbg.h: Adjust prototype.
* NEWS: Mention new switch

binutils/ChangeLog
binutils/NEWS
binutils/budbg.h
binutils/doc/binutils.texi
binutils/objdump.c
binutils/prdbg.c

index 11a79ce..38b3b87 100644 (file)
@@ -1,3 +1,12 @@
+2003-07-22  Salvador Eduardo Tropea <set@computer.org>
+
+       * objdump.c: New command line option --debugging-tags.
+       * doc/binutils.texi: Document new command line option.
+       * prdbg.c: Code to print the debug info as tags compatible 
+       with ctags.
+       * budbg.h: Adjust prototype.
+       * NEWS: Mention new switch
+        
 2003-07-18  Nick Clifton  <nickc@redhat.com>
 
        * objdump.c (main) :Accept multiple -M switch.
index 4338946..0a8c0cf 100644 (file)
@@ -1,5 +1,8 @@
 -*- text -*-
 
+* objdump now accepts --debugging-tags to print the debug information in a
+  format compatible with ctags tool.
+
 * objcopy and strip now accept --only-keep-debug to create a file containing
   those sections that would be stripped out by --strip-debug.  The idea is that
   this can be used in conjunction with the --add-gnu-debuglink switch to create
index dc68703..e4eee83 100644 (file)
@@ -1,5 +1,5 @@
 /* budbg.c -- Interfaces to the generic debugging information routines.
-   Copyright 1995, 1996, 2002 Free Software Foundation, Inc.
+   Copyright 1995, 1996, 2002, 2003 Free Software Foundation, Inc.
    Written by Ian Lance Taylor <ian@cygnus.com>.
 
    This file is part of GNU Binutils.
 
 /* Routine used to read generic debugging information.  */
 
-extern PTR read_debugging_info
+extern PTR read_debugging_info 
   PARAMS ((bfd *, asymbol **, long));
-
+  
 /* Routine used to print generic debugging information.  */
 
 extern bfd_boolean print_debugging_info
-  PARAMS ((FILE *, PTR));
+  (FILE *, void *, bfd *, asymbol **, void *, bfd_boolean);
 
 /* Routines used to read and write stabs information.  */
 
index 256acf0..f86169d 100644 (file)
@@ -1435,6 +1435,7 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{-f}|@option{--file-headers}]
         [@option{--file-start-context}]
         [@option{-g}|@option{--debugging}]
+        [@option{-e}|@option{--debugging-tags}]
         [@option{-h}|@option{--section-headers}|@option{--headers}]
         [@option{-i}|@option{--info}]
         [@option{-j} @var{section}|@option{--section=}@var{section}]
@@ -1480,7 +1481,7 @@ object files.
 
 The long and short forms of options, shown here as alternatives, are
 equivalent.  At least one option from the list
-@option{-a,-d,-D,-f,-g,-G,-h,-H,-p,-r,-R,-S,-t,-T,-V,-x} must be given. 
+@option{-a,-d,-D,-e,-f,-g,-G,-h,-H,-p,-r,-R,-S,-t,-T,-V,-x} must be given.
 
 @table @env
 @item -a
@@ -1536,6 +1537,11 @@ Only certain types of debugging information have been implemented.
 Some other types are supported by @command{readelf -w}.
 @xref{readelf}.
 
+@item -e
+@itemx --debugging-tags
+Like @option{-g}, but the information is generated in a format compatible
+with ctags tool.
+
 @item -d
 @itemx --disassemble
 @cindex disassembling object code
index 47da66f..8011b58 100644 (file)
@@ -74,6 +74,7 @@ static int wide_output;                       /* -w */
 static bfd_vma start_address = (bfd_vma) -1; /* --start-address */
 static bfd_vma stop_address = (bfd_vma) -1;  /* --stop-address */
 static int dump_debugging;             /* --debugging */
+static int dump_debugging_tags;                /* --debugging-tags */
 static bfd_vma adjust_section_vma = 0; /* --adjust-vma */
 static int file_start_context = 0;      /* --file-start-context */
 
@@ -208,6 +209,7 @@ usage (stream, status)
   -S, --source             Intermix source code with disassembly\n\
   -s, --full-contents      Display the full contents of all sections requested\n\
   -g, --debugging          Display debug information in object file\n\
+  -e, --debugging-tags     Display debug information using ctags style\n\
   -G, --stabs              Display (in raw form) any STABS info in the file\n\
   -t, --syms               Display the contents of the symbol table(s)\n\
   -T, --dynamic-syms       Display the contents of the dynamic symbol table\n\
@@ -266,6 +268,7 @@ static struct option long_options[]=
   {"architecture", required_argument, NULL, 'm'},
   {"archive-headers", no_argument, NULL, 'a'},
   {"debugging", no_argument, NULL, 'g'},
+  {"debugging-tags", no_argument, NULL, 'e'},
   {"demangle", optional_argument, NULL, 'C'},
   {"disassemble", no_argument, NULL, 'd'},
   {"disassemble-all", no_argument, NULL, 'D'},
@@ -2068,15 +2071,17 @@ dump_bfd (abfd)
        }
     }
 
-  printf (_("\n%s:     file format %s\n"), bfd_get_filename (abfd),
-         abfd->xvec->name);
+  if (! dump_debugging_tags)
+    printf (_("\n%s:     file format %s\n"), bfd_get_filename (abfd),
+           abfd->xvec->name);
   if (dump_ar_hdrs)
     print_arelt_descr (stdout, abfd, TRUE);
   if (dump_file_header)
     dump_bfd_header (abfd);
   if (dump_private_headers)
     dump_bfd_private_header (abfd);
-  putchar ('\n');
+  if (! dump_debugging_tags)
+    putchar ('\n');
   if (dump_section_headers)
     dump_headers (abfd);
 
@@ -2106,7 +2111,8 @@ dump_bfd (abfd)
       dhandle = read_debugging_info (abfd, syms, symcount);
       if (dhandle != NULL)
        {
-         if (! print_debugging_info (stdout, dhandle))
+         if (! print_debugging_info (stdout, dhandle, abfd, syms, demangle,
+             dump_debugging_tags ? TRUE : FALSE))
            {
              non_fatal (_("%s: printing debugging information failed"),
                         bfd_get_filename (abfd));
@@ -2648,7 +2654,7 @@ main (argc, argv)
   bfd_init ();
   set_default_bfd_target ();
 
-  while ((c = getopt_long (argc, argv, "pib:m:M:VvCdDlfaHhrRtTxsSj:wE:zgG",
+  while ((c = getopt_long (argc, argv, "pib:m:M:VvCdDlfaHhrRtTxsSj:wE:zgeG",
                           long_options, (int *) 0))
         != EOF)
     {
@@ -2785,6 +2791,12 @@ main (argc, argv)
          dump_debugging = 1;
          seenflag = TRUE;
          break;
+       case 'e':
+         dump_debugging = 1;
+         dump_debugging_tags = 1;
+         do_demangle = TRUE;
+         seenflag = TRUE;
+         break;
        case 'G':
          dump_stab_section_info = TRUE;
          seenflag = TRUE;
index b29eec6..33ba213 100644 (file)
@@ -1,6 +1,7 @@
 /* prdbg.c -- Print out generic debugging information.
-   Copyright 1995, 1996, 2002 Free Software Foundation, Inc.
+   Copyright 1995, 1996, 2002, 2003 Free Software Foundation, Inc.
    Written by Ian Lance Taylor <ian@cygnus.com>.
+   Tags style generation written by Salvador E. Tropea <set@computer.org>.
 
    This file is part of GNU Binutils.
 
@@ -43,6 +44,15 @@ struct pr_handle
   struct pr_stack *stack;
   /* Parameter number we are about to output.  */
   int parameter;
+  /* The following are used only by the tags code (tg_).  */
+  /* Name of the file we are using.  */
+  char *filename;
+  /* The BFD.  */
+  bfd *abfd;
+  /* The symbols table for this BFD.  */
+  asymbol **syms;
+  /* Pointer to a function to demangle symbols.  */
+  char *(*demangler) (bfd *, const char *);
 };
 
 /* The type stack.  */
@@ -57,6 +67,13 @@ struct pr_stack
   enum debug_visibility visibility;
   /* Name of the current method we are handling.  */
   const char *method;
+  /* The following are used only by the tags code (tg_).  */
+  /* Type for the container (struct, union, class, union class).  */
+  const char *flavor;
+  /* A comma separated list of parent classes.  */
+  char *parents;
+  /* How many parents contains parents.  */
+  int num_parents;
 };
 
 static void indent
@@ -168,7 +185,40 @@ static bfd_boolean pr_end_function
   PARAMS ((PTR));
 static bfd_boolean pr_lineno
   PARAMS ((PTR, const char *, unsigned long, bfd_vma));
-
+static bfd_boolean append_parent (struct pr_handle *, const char *);
+/* Only used by tg_ code.  */
+static bfd_boolean tg_fix_visibility (struct pr_handle *, enum debug_visibility);
+static void find_address_in_section (bfd *, asection *, void *);
+static void translate_addresses (bfd *, char *, FILE *, asymbol **);
+static const char *visibility_name (enum debug_visibility);
+/* Tags style replacements.  */
+static bfd_boolean tg_start_compilation_unit (void *, const char *);
+static bfd_boolean tg_start_source (void *, const char *);
+static bfd_boolean tg_enum_type (void *, const char *, const char **, bfd_signed_vma *);
+static bfd_boolean tg_start_struct_type (void *, const char *, unsigned int, bfd_boolean, unsigned int);
+static bfd_boolean pr_struct_field (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility);
+static bfd_boolean tg_struct_field (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility);
+static bfd_boolean tg_struct_field  (void *, const char *, bfd_vma, bfd_vma, enum debug_visibility);
+static bfd_boolean tg_end_struct_type (void *);
+static bfd_boolean tg_start_class_type (void *, const char *, unsigned int, bfd_boolean, unsigned int, bfd_boolean, bfd_boolean);
+static bfd_boolean tg_class_static_member (void *, const char *, const char *, enum debug_visibility);
+static bfd_boolean tg_class_baseclass (void *, bfd_vma, bfd_boolean, enum debug_visibility);
+static bfd_boolean tg_class_method_variant (void *, const char *, enum debug_visibility, bfd_boolean, bfd_boolean, bfd_vma, bfd_boolean);
+static bfd_boolean tg_class_static_method_variant (void *, const char *, enum debug_visibility, bfd_boolean, bfd_boolean);
+static bfd_boolean tg_end_class_type (void *);
+static bfd_boolean tg_tag_type (void *, const char *, unsigned int, enum debug_type_kind);
+static bfd_boolean tg_typdef (void *, const char *);
+static bfd_boolean tg_tag (void *, const char *);
+static bfd_boolean tg_int_constant (void *, const char *, bfd_vma);
+static bfd_boolean tg_float_constant (void *, const char *, double);
+static bfd_boolean tg_typed_constant (void *, const char *, bfd_vma);
+static bfd_boolean tg_variable (void *, const char *, enum debug_var_kind, bfd_vma);
+static bfd_boolean tg_start_function (void *, const char *, bfd_boolean);
+static bfd_boolean tg_function_parameter (void *, const char *, enum debug_parm_kind, bfd_vma);
+static bfd_boolean tg_start_block (void *, bfd_vma);
+static bfd_boolean tg_end_block (void *, bfd_vma);
+static bfd_boolean tg_lineno (void *, const char *, unsigned long, bfd_vma);
+\f
 static const struct debug_write_fns pr_fns =
 {
   pr_start_compilation_unit,
@@ -217,12 +267,64 @@ static const struct debug_write_fns pr_fns =
   pr_lineno
 };
 \f
+static const struct debug_write_fns tg_fns =
+{
+  tg_start_compilation_unit,
+  tg_start_source,
+  pr_empty_type,               /* Same, push_type.  */
+  pr_void_type,                        /* Same, push_type.  */
+  pr_int_type,                 /* Same, push_type.  */
+  pr_float_type,               /* Same, push_type.  */
+  pr_complex_type,             /* Same, push_type.  */
+  pr_bool_type,                        /* Same, push_type.  */
+  tg_enum_type,
+  pr_pointer_type,             /* Same, changes to pointer.  */
+  pr_function_type,            /* Same, push_type.  */
+  pr_reference_type,           /* Same, changes to reference.  */
+  pr_range_type,               /* FIXME: What's that?.  */
+  pr_array_type,               /* Same, push_type.  */
+  pr_set_type,                 /* FIXME: What's that?.  */
+  pr_offset_type,              /* FIXME: What's that?.  */
+  pr_method_type,              /* Same.  */
+  pr_const_type,               /* Same, changes to const.  */
+  pr_volatile_type,            /* Same, changes to volatile.  */
+  tg_start_struct_type,
+  tg_struct_field,
+  tg_end_struct_type,
+  tg_start_class_type,
+  tg_class_static_member,
+  tg_class_baseclass,
+  pr_class_start_method,       /* Same, remmembers that's a method.  */
+  tg_class_method_variant,
+  tg_class_static_method_variant,
+  pr_class_end_method,         /* Same, forgets that's a method.  */
+  tg_end_class_type,
+  pr_typedef_type,             /* Same, just push type.  */
+  tg_tag_type,
+  tg_typdef,
+  tg_tag,
+  tg_int_constant,             /* Untested.  */
+  tg_float_constant,           /* Untested.  */
+  tg_typed_constant,           /* Untested.  */
+  tg_variable,
+  tg_start_function,
+  tg_function_parameter,
+  tg_start_block,
+  tg_end_block,
+  pr_end_function,             /* Same, does nothing.  */
+  tg_lineno
+};
+\f
 /* Print out the generic debugging information recorded in dhandle.  */
 
 bfd_boolean
-print_debugging_info (f, dhandle)
+print_debugging_info (f, dhandle, abfd, syms, demangler, as_tags)
      FILE *f;
      PTR dhandle;
+     bfd *abfd;
+     asymbol **syms;
+     PTR demangler;
+     bfd_boolean as_tags;
 {
   struct pr_handle info;
 
@@ -230,8 +332,21 @@ print_debugging_info (f, dhandle)
   info.indent = 0;
   info.stack = NULL;
   info.parameter = 0;
+  info.filename = NULL;
+  info.abfd = abfd;
+  info.syms = syms;
+  info.demangler = demangler;
+
+  if (as_tags)
+    {
+      fputs ("!_TAG_FILE_FORMAT\t2\t/extended format/\n", f);
+      fputs ("!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted/\n", f);
+      fputs ("!_TAG_PROGRAM_AUTHOR\tIan Lance Taylor, Salvador E. Tropea and others\t//\n", f);
+      fputs ("!_TAG_PROGRAM_NAME\tobjdump\t/From GNU binutils/\n", f);
+    }
 
-  return debug_write (dhandle, &pr_fns, (PTR) &info);
+  return as_tags ? debug_write (dhandle, &tg_fns, (void *) & info)
+    : debug_write (dhandle, &pr_fns, (void *) & info);
 }
 \f
 /* Indent to the current indentation level.  */
@@ -311,6 +426,26 @@ append_type (info, s)
   return TRUE;
 }
 
+/* Append a string to the parents on the top of the type stack.  */
+
+static bfd_boolean
+append_parent (struct pr_handle *info, const char *s)
+{
+  unsigned int len;
+
+  if (s == NULL)
+    return FALSE;
+
+  assert (info->stack != NULL);
+
+  len = info->stack->parents ? strlen (info->stack->parents) : 0;
+  info->stack->parents = (char *) xrealloc (info->stack->parents,
+                                           len + strlen (s) + 1);
+  strcpy (info->stack->parents + 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.  */
@@ -1894,3 +2029,922 @@ pr_end_function (p)
 {
   return TRUE;
 }
+\f
+/* Tags style generation functions start here.  */
+
+/* Variables for address to line translation.  */
+static bfd_vma pc;
+static const char *filename;
+static const char *functionname;
+static unsigned int line;
+static bfd_boolean found;
+
+/* Look for an address in a section.  This is called via
+   bfd_map_over_sections.  */
+
+static void
+find_address_in_section (bfd *abfd, asection *section, void *data)
+{
+  bfd_vma vma;
+  bfd_size_type size;
+  asymbol **syms = (asymbol **) data;
+
+  if (found)
+    return;
+
+  if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0)
+    return;
+
+  vma = bfd_get_section_vma (abfd, section);
+  if (pc < vma)
+    return;
+
+  size = bfd_get_section_size_before_reloc (section);
+  if (pc >= vma + size)
+    return;
+
+  found = bfd_find_nearest_line (abfd, section, syms, pc - vma,
+                                &filename, &functionname, &line);
+}
+
+static void
+translate_addresses (bfd *abfd, char *addr_hex, FILE *f, asymbol **syms)
+{
+  pc = bfd_scan_vma (addr_hex, NULL, 16);
+  found = FALSE;
+  bfd_map_over_sections (abfd, find_address_in_section, syms);
+
+  if (! found)
+    fprintf (f, "??");
+  else
+    fprintf (f, "%u", line);
+}
+
+/* Start a new compilation unit.  */
+
+static bfd_boolean
+tg_start_compilation_unit (void * p, const char *filename ATTRIBUTE_UNUSED)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  fprintf (stderr, "New compilation unit: %s\n", filename);
+
+  free (info->filename);
+  /* Should it be relative? best way to do it here?.  */
+  info->filename = strdup (filename);
+
+  return TRUE;
+}
+
+/* Start a source file within a compilation unit.  */
+
+static bfd_boolean
+tg_start_source (void *p, const char *filename)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  free (info->filename);
+  /* Should it be relative? best way to do it here?.  */
+  info->filename = strdup (filename);
+
+  return TRUE;
+}
+
+/* Push an enum type onto the type stack.  */
+
+static bfd_boolean
+tg_enum_type (void *p, const char *tag, const char **names,
+             bfd_signed_vma *values)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  unsigned int i;
+  const char *name;
+  char ab[20];
+
+  if (! pr_enum_type (p, tag, names, values))
+    return FALSE;
+
+  name = tag ? tag : "unknown";
+  /* Generate an entry for the enum.  */
+  if (tag)
+    fprintf (info->f, "%s\t%s\t0;\"\tkind:e\ttype:%s\n", tag,
+            info->filename, info->stack->type);
+
+  /* Generate entries for the values.  */
+  if (names != NULL)
+    {
+      for (i = 0; names[i] != NULL; i++)
+       {
+         print_vma (values[i], ab, FALSE, FALSE);
+         fprintf (info->f, "%s\t%s\t0;\"\tkind:g\tenum:%s\tvalue:%s\n",
+                  names[i], info->filename, name, ab);
+       }
+    }
+
+  return TRUE;
+}
+
+/* Start accumulating a struct type.  */
+
+static bfd_boolean
+tg_start_struct_type (void *p, const char *tag, unsigned int id,
+                     bfd_boolean structp, unsigned int size ATTRIBUTE_UNUSED)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  const char *name;
+  char idbuf[20];
+
+  if (tag != NULL)
+    name = tag;
+  else
+    {
+      name = idbuf;
+      sprintf (idbuf, "%%anon%u", id);
+    }
+
+  if (! push_type (info, name))
+    return FALSE;
+
+  info->stack->flavor = structp ? "struct" : "union";
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:%c\n", name, info->filename,
+          info->stack->flavor[0]);
+
+  info->stack->visibility = DEBUG_VISIBILITY_PUBLIC;
+
+  return indent_type (info);
+}
+
+/* Output the visibility of a field in a struct.  */
+
+static bfd_boolean
+tg_fix_visibility (struct pr_handle *info, enum debug_visibility visibility)
+{
+  assert (info->stack != NULL);
+
+  if (info->stack->visibility == visibility)
+    return TRUE;
+
+  assert (info->stack->visibility != DEBUG_VISIBILITY_IGNORE);
+
+  info->stack->visibility = visibility;
+
+  return TRUE;
+}
+
+/* Add a field to a struct type.  */
+
+static bfd_boolean
+tg_struct_field (void *p, const char *name, bfd_vma bitpos ATTRIBUTE_UNUSED,
+                bfd_vma bitsize ATTRIBUTE_UNUSED,
+                enum debug_visibility visibility)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return FALSE;
+
+  if (! tg_fix_visibility (info, visibility))
+    return FALSE;
+
+  /* It happends, a bug? */
+  if (! name[0])
+    return TRUE;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:m\ttype:%s\t%s:%s\taccess:%s\n",
+          name, info->filename, t, info->stack->flavor, info->stack->type,
+          visibility_name (visibility));
+
+  return TRUE;
+}
+
+/* Finish a struct type.  */
+
+static bfd_boolean
+tg_end_struct_type (void *p ATTRIBUTE_UNUSED)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  assert (info->stack != NULL);
+
+  return TRUE;
+}
+
+/* Start a class type.  */
+
+static bfd_boolean
+tg_start_class_type (void *p, const char *tag, unsigned int id,
+                    bfd_boolean structp, unsigned int size,
+                    bfd_boolean vptr, bfd_boolean ownvptr)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *tv = NULL;
+  const char *name;
+
+  info->indent += 2;
+
+  if (vptr && ! ownvptr)
+    {
+      tv = pop_type (info);
+      if (tv == NULL)
+       return FALSE;
+    }
+
+  if (tag != NULL)
+    name = tag;
+  else
+    {
+      char idbuf[20];
+
+      sprintf (idbuf, "%%anon%u", id);
+      name = idbuf;
+    }
+
+  if (! push_type (info, name))
+    return FALSE;
+
+  info->stack->flavor = structp ? "class" : "union class";
+  info->stack->parents = NULL;
+  info->stack->num_parents = 0;
+
+  if (size != 0 || vptr || ownvptr || tag != NULL)
+    {
+      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;
+           }
+       }
+    }
+
+  info->stack->visibility = DEBUG_VISIBILITY_PRIVATE;
+
+  return TRUE;
+}
+
+/* Add a static member to a class.  */
+
+static bfd_boolean
+tg_class_static_member (void *p, const char *name,
+                       const char *physname ATTRIBUTE_UNUSED,
+                       enum debug_visibility visibility)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  int len_var, len_class;
+  char *full_name;
+
+  len_var = strlen (name);
+  len_class = strlen (info->stack->next->type);
+  full_name = (char *) xmalloc (len_var + len_class + 3);
+  if (! full_name)
+    return FALSE;
+  memcpy (full_name, info->stack->next->type, len_class);
+  memcpy (full_name + len_class, "::", 2);
+  memcpy (full_name + len_class + 2, name, len_var + 1);
+
+  if (! substitute_type (info, full_name))
+    return FALSE;
+
+  if (! prepend_type (info, "static "))
+    return FALSE;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return FALSE;
+
+  if (! tg_fix_visibility (info, visibility))
+    return FALSE;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:x\ttype:%s\tclass:%s\taccess:%s\n",
+          name, info->filename, t, info->stack->type,
+          visibility_name (visibility));
+  free (t);
+  free (full_name);
+
+  return TRUE;
+}
+
+/* Add a base class to a class.  */
+
+static bfd_boolean
+tg_class_baseclass (void *p, bfd_vma bitpos ATTRIBUTE_UNUSED,
+                   bfd_boolean virtual, enum debug_visibility visibility)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  const char *prefix;
+
+  assert (info->stack != NULL && info->stack->next != NULL);
+
+  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;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return FALSE;
+
+  if (info->stack->num_parents && ! append_parent (info, ", "))
+    return FALSE;
+
+  if (! append_parent (info, t))
+    return FALSE;
+  info->stack->num_parents++;
+
+  free (t);
+
+  return TRUE;
+}
+
+/* Add a variant to a method.  */
+
+static bfd_boolean
+tg_class_method_variant (void *p, const char *physname ATTRIBUTE_UNUSED,
+                        enum debug_visibility visibility,
+                        bfd_boolean constp, bfd_boolean volatilep,
+                        bfd_vma voffset ATTRIBUTE_UNUSED,
+                        bfd_boolean context)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *method_type;
+  char *context_type;
+  char *method_name;
+
+  assert (info->stack != NULL);
+  assert (info->stack->next != NULL);
+
+  /* Put the const and volatile qualifiers on the type.  */
+  if (volatilep)
+    {
+      if (! append_type (info, " volatile"))
+       return FALSE;
+    }
+  if (constp)
+    {
+      if (! append_type (info, " const"))
+       return FALSE;
+    }
+
+  method_name = strdup (context ? info->stack->next->next->method
+                       : info->stack->next->method);
+  
+  /* Stick the name of the method into its type.  */
+  if (! substitute_type (info, method_name))
+    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 class.  */
+  if (! tg_fix_visibility (info, visibility))
+    return FALSE;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:p\ttype:%s\tclass:%s\n",
+          method_name, info->filename, method_type, info->stack->type);
+  free (method_type);
+  free (method_name);
+  free (context_type);
+  
+  return TRUE;
+}
+
+/* Add a static variant to a method.  */
+
+static bfd_boolean
+tg_class_static_method_variant (void *p,
+                               const char *physname ATTRIBUTE_UNUSED,
+                               enum debug_visibility visibility,
+                               bfd_boolean constp,
+                               bfd_boolean volatilep)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *method_type;
+  char *method_name;
+
+  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 (! append_type (info, " volatile"))
+       return FALSE;
+    }
+  if (constp)
+    {
+      if (! append_type (info, " const"))
+       return FALSE;
+    }
+
+  /* Mark it as static.  */
+  if (! prepend_type (info, "static "))
+    return FALSE;
+
+  method_name = strdup (info->stack->next->method);
+  /* 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 class.  */
+  if (! tg_fix_visibility (info, visibility))
+    return FALSE;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:p\ttype:%s\tclass:%s\taccess:%s\n",
+          method_name, info->filename, method_type, info->stack->type,
+          visibility_name (visibility));
+  free (method_type);
+  free (method_name);
+
+  return TRUE;
+}
+
+/* Finish up a class.  */
+
+static bfd_boolean
+tg_end_class_type (void *p)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:c\ttype:%s", info->stack->type,
+          info->filename, info->stack->flavor);
+  if (info->stack->num_parents)
+    {
+      fprintf  (info->f, "\tinherits:%s", info->stack->parents);
+      free (info->stack->parents);
+    }
+  fputc ('\n', info->f);
+
+  return tg_end_struct_type (p);
+}
+
+/* Push a type on the stack using a tag name.  */
+
+static bfd_boolean
+tg_tag_type (void *p, const char *name, unsigned int id,
+            enum debug_type_kind kind)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  const char *t, *tag;
+  char idbuf[20];
+
+  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;
+    }
+
+  if (! push_type (info, t))
+    return FALSE;
+  if (name != NULL)
+    tag = name;
+  else
+    {
+      sprintf (idbuf, "%%anon%u", id);
+      tag = idbuf;
+    }
+
+  if (! append_type (info, tag))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Output a typedef.  */
+
+static bfd_boolean
+tg_typdef (void *p, const char *name)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *s;
+
+  s = pop_type (info);
+  if (s == NULL)
+    return FALSE;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:t\ttype:%s\n", name,
+          info->filename, s);
+
+  free (s);
+
+  return TRUE;
+}
+
+/* Output a tag.  The tag should already be in the string on the
+   stack, so all we have to do here is print it out.  */
+
+static bfd_boolean
+tg_tag (void *p ATTRIBUTE_UNUSED, const char *name ATTRIBUTE_UNUSED)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return FALSE;
+  free (t);
+
+  return TRUE;
+}
+
+/* Output an integer constant.  */
+
+static bfd_boolean
+tg_int_constant (void *p, const char *name, bfd_vma val)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[20];
+
+  indent (info);
+  print_vma (val, ab, FALSE, FALSE);
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:v\ttype:const int\tvalue:%s\n",
+          name, info->filename, ab);
+  return TRUE;
+}
+
+/* Output a floating point constant.  */
+
+static bfd_boolean
+tg_float_constant (void *p, const char *name, double val)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+
+  indent (info);
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:v\ttype:const double\tvalue:%g\n",
+          name, info->filename, val);
+  return TRUE;
+}
+
+/* Output a typed constant.  */
+
+static bfd_boolean
+tg_typed_constant (void *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, "%s\t%s\t0;\"\tkind:v\ttype:const %s\tvalue:%s\n",
+          name, info->filename, t, ab);
+
+  free (t);
+
+  return TRUE;
+}
+
+/* Output a variable.  */
+
+static bfd_boolean
+tg_variable (void *p, const char *name, enum debug_var_kind kind,
+            bfd_vma val ATTRIBUTE_UNUSED)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+  const char *dname, *from_class;
+
+  t = pop_type (info);
+  if (t == NULL)
+    return FALSE;
+
+  dname = name;
+  if (info->demangler)
+    {
+      dname = info->demangler (info->abfd, name);
+      if (strcmp (name, dname) == 0)
+       {
+         free ((char *) dname);
+         dname = name;
+       }
+    }
+
+  if (dname != name)
+    {
+      char *sep;
+      sep = strstr (dname, "::");
+      if (sep)
+       {
+         *sep = 0;
+         name = sep + 2;
+         from_class = dname;
+       }
+      else
+       {
+         /* Obscure types as vts and type_info nodes.  */
+         name = dname;
+         from_class = NULL;
+       }
+    }
+  else
+    from_class = NULL;
+
+  fprintf (info->f, "%s\t%s\t0;\"\tkind:v\ttype:%s", name, info->filename, t);
+
+  switch (kind)
+    {
+    case DEBUG_STATIC:
+    case DEBUG_LOCAL_STATIC:
+      fprintf (info->f, "\tfile:");
+      break;
+    case DEBUG_REGISTER:
+      fprintf (info->f, "\tregister:");
+      break;
+    default:
+      break;
+    }
+
+  if (from_class)
+    {
+      fprintf (info->f, "\tclass:%s",from_class);
+      free ((char *) dname);
+    }
+
+  fprintf (info->f, "\n");
+
+  free (t);
+
+  return TRUE;
+}
+
+/* Start outputting a function.  */
+
+static bfd_boolean
+tg_start_function (void *p, const char *name, bfd_boolean global)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  const char *dname;
+
+  if (! global)
+    info->stack->flavor = "static";
+  else
+    info->stack->flavor = NULL;
+
+  dname = name;
+  if (info->demangler)
+    {
+      dname = info->demangler (info->abfd, name);
+      if (strcmp (name, dname) == 0)
+       {
+         free ((char *) dname);
+         dname = name;
+       }
+    }
+
+  if (! substitute_type (info, dname))
+    return FALSE;
+    
+  if (dname != name)
+    {
+      char *sep;
+      sep = strstr (dname, "::");
+      if (sep)
+       {
+         info->stack->method = dname;
+         *sep = 0;
+         name = sep + 2;
+       }
+      else
+       {
+         info->stack->method = "";
+         name = dname;
+       }
+      sep = strchr (name, '(');
+      if (sep)
+       *sep = 0;
+      /* Obscure functions as type_info function.  */
+    }
+  else
+    info->stack->method = NULL;
+
+  info->stack->parents = strdup (name);
+
+  if (! info->stack->method && ! append_type (info, "("))
+    return FALSE;
+
+  info->parameter = 1;
+
+  return TRUE;
+}
+
+/* Output a function parameter.  */
+
+static bfd_boolean
+tg_function_parameter (void *p, const char *name, enum debug_parm_kind kind,
+                      bfd_vma val ATTRIBUTE_UNUSED)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char *t;
+
+  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->stack->method)
+    {
+      if (info->parameter != 1 && ! append_type (info, ", "))
+       return FALSE;
+    
+      if (kind == DEBUG_PARM_REG || kind == DEBUG_PARM_REF_REG)
+       if (! append_type (info, "register "))
+         return FALSE;
+          
+      if (! append_type (info, t))
+       return FALSE;
+    }
+
+  free (t);
+
+  ++info->parameter;
+
+  return TRUE;
+}
+
+/* Start writing out a block.  */
+
+static bfd_boolean
+tg_start_block (void *p, bfd_vma addr)
+{
+  struct pr_handle *info = (struct pr_handle *) p;
+  char ab[20], kind, *partof;
+  char *t;
+  bfd_boolean local;
+
+  if (info->parameter > 0)
+    {
+      info->parameter = 0;
+
+      /* Delayed name.  */
+      fprintf (info->f, "%s\t%s\t", info->stack->parents, info->filename);
+      free (info->stack->parents);
+
+      print_vma (addr, ab, TRUE, TRUE);
+      translate_addresses (info->abfd, ab, info->f, info->syms);
+      local = info->stack->flavor != NULL;
+      if (info->stack->method && *info->stack->method)
+       {
+         kind = 'm';
+         partof = (char *) info->stack->method;
+       }
+      else
+       {
+         kind = 'f';
+         partof = NULL;
+         if (! info->stack->method && ! append_type (info, ")"))
+           return FALSE;
+       }
+      t = pop_type (info);
+      if (t == NULL)
+       return FALSE;
+      fprintf (info->f, ";\"\tkind:%c\ttype:%s", kind, t);
+      if (local)
+       fputs ("\tfile:", info->f);
+      if (partof)
+       {
+         fprintf (info->f, "\tclass:%s", partof);
+         free (partof);
+       }
+      fputc ('\n', info->f);
+    }
+
+  return TRUE;
+}
+
+/* Write out line number information.  */
+
+static bfd_boolean
+tg_lineno (void *p ATTRIBUTE_UNUSED,
+          const char *filename ATTRIBUTE_UNUSED,
+          unsigned long lineno ATTRIBUTE_UNUSED,
+          bfd_vma addr ATTRIBUTE_UNUSED)
+{
+  return TRUE;
+}
+
+/* Finish writing out a block.  */
+
+static bfd_boolean
+tg_end_block (void *p ATTRIBUTE_UNUSED, bfd_vma addr ATTRIBUTE_UNUSED)
+{
+  return TRUE;
+}
+
+/* Convert the visibility value into a human readable name.  */
+
+static const char *
+visibility_name (enum debug_visibility visibility)
+{
+  const char *s;
+
+  switch (visibility)
+    {
+    case DEBUG_VISIBILITY_PUBLIC:
+      s = "public";
+      break;
+    case DEBUG_VISIBILITY_PRIVATE:
+      s = "private";
+      break;
+    case DEBUG_VISIBILITY_PROTECTED:
+      s = "protected";
+      break;
+    case DEBUG_VISIBILITY_IGNORE:
+      s = "/* ignore */";
+      break;
+    default:
+      abort ();
+      return FALSE;
+    }
+  return s;
+}