2009-11-24 Tristan Gingold <gingold@adacore.com>
[binutils-gdb.git] / bfd / vms-hdr.c
index f116904f2373f5c46d7bac7ba074878e62dfbb38..407fa7fdff98928a0e5abcb514c76a3c56ffbfec 100644 (file)
@@ -1,19 +1,36 @@
 /* vms-hdr.c -- BFD back-end for VMS/VAX (openVMS/VAX) and
    EVAX (openVMS/Alpha) files.
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006,
 /* vms-hdr.c -- BFD back-end for VMS/VAX (openVMS/VAX) and
    EVAX (openVMS/Alpha) files.
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006,
-   2007 Free Software Foundation, Inc.
+   2007, 2008, 2009 Free Software Foundation, Inc.
 
    HDR record handling functions
    EMH record handling functions
 
    HDR record handling functions
    EMH record handling functions
-   and
+
    EOM record handling functions
    EEOM record handling functions
 
    EOM record handling functions
    EEOM record handling functions
 
+   IHD record handling functions
+   EIHD record handling functions
+
+   ISD record handling functions
+   EISD record handling functions
+
+   IHS record handling functions
+   EIHS record handling functions
+
+   DBG record handling functions
+   EDBG record handling functions
+
+   TBT record handling functions
+   ETBT record handling functions
+
+   DST/DMT section handling functions
+
    Written by Klaus K"ampf (kkaempf@rmi.de)
 
    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
    Written by Klaus K"ampf (kkaempf@rmi.de)
 
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -23,7 +40,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
 
    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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 #include "sysdep.h"
 #include "bfd.h"
 
 #include "sysdep.h"
 #include "bfd.h"
 
 #include "vms.h"
 
 
 #include "vms.h"
 
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
+static struct module *new_module (bfd *);
+static void parse_module
+  (bfd *, struct module *, unsigned char *, unsigned int);
+static struct module *build_module_list (bfd *);
+static bfd_boolean module_find_nearest_line
+  (bfd *, struct module *, bfd_vma, const char **, const char **,
+   unsigned int *);
+static int vms_slurp_debug (bfd *);
+
+#define SET_MODULE_PARSED(m) \
+  do { if ((m)->name == NULL) (m)->name = ""; } while (0)
+#define IS_MODULE_PARSED(m) ((m)->name != NULL)
+
 
 /* Read & process emh record
    return 0 on success, -1 on error.  */
 
 /* Read & process emh record
    return 0 on success, -1 on error.  */
@@ -84,15 +112,15 @@ _bfd_vms_slurp_hdr (bfd *abfd, int objtype)
       break;
 
     case MHD_S_C_LNM:
       break;
 
     case MHD_S_C_LNM:
-      PRIV (hdr_data).hdr_c_lnm = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_length - 2));
+      PRIV (hdr_data).hdr_c_lnm = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_size - 2));
       break;
 
     case MHD_S_C_SRC:
       break;
 
     case MHD_S_C_SRC:
-      PRIV (hdr_data).hdr_c_src = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_length - 2));
+      PRIV (hdr_data).hdr_c_src = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_size - 2));
       break;
 
     case MHD_S_C_TTL:
       break;
 
     case MHD_S_C_TTL:
-      PRIV (hdr_data).hdr_c_ttl = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_length - 2));
+      PRIV (hdr_data).hdr_c_ttl = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_size - 2));
       break;
 
     case EMH_S_C_MHD + EVAX_OFFSET:
       break;
 
     case EMH_S_C_MHD + EVAX_OFFSET:
@@ -109,15 +137,15 @@ _bfd_vms_slurp_hdr (bfd *abfd, int objtype)
       break;
 
     case EMH_S_C_LNM + EVAX_OFFSET:
       break;
 
     case EMH_S_C_LNM + EVAX_OFFSET:
-      PRIV (hdr_data).hdr_c_lnm = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_length - 6));
+      PRIV (hdr_data).hdr_c_lnm = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_size - 6));
       break;
 
     case EMH_S_C_SRC + EVAX_OFFSET:
       break;
 
     case EMH_S_C_SRC + EVAX_OFFSET:
-      PRIV (hdr_data).hdr_c_src = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_length - 6));
+      PRIV (hdr_data).hdr_c_src = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_size - 6));
       break;
 
     case EMH_S_C_TTL + EVAX_OFFSET:
       break;
 
     case EMH_S_C_TTL + EVAX_OFFSET:
-      PRIV (hdr_data).hdr_c_ttl = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_length - 6));
+      PRIV (hdr_data).hdr_c_ttl = _bfd_vms_save_sized_string (vms_rec, PRIV (rec_size - 6));
       break;
 
     case MHD_S_C_CPR:
       break;
 
     case MHD_S_C_CPR:
@@ -188,6 +216,7 @@ _bfd_vms_write_hdr (bfd *abfd, int objtype)
   unsigned int symnum;
   int had_case = 0;
   int had_file = 0;
   unsigned int symnum;
   int had_case = 0;
   int had_file = 0;
+  char version [256];
 
 #if VMS_DEBUG
   vms_debug (2, "vms_write_hdr (%p)\n", abfd);
 
 #if VMS_DEBUG
   vms_debug (2, "vms_write_hdr (%p)\n", abfd);
@@ -205,50 +234,59 @@ _bfd_vms_write_hdr (bfd *abfd, int objtype)
       _bfd_vms_output_long (abfd, MAX_OUTREC_SIZE);
     }
 
       _bfd_vms_output_long (abfd, MAX_OUTREC_SIZE);
     }
 
+  /* Create module name from filename.  */
   if (bfd_get_filename (abfd) != 0)
     {
       /* Strip path and suffix information.  */
       char *fname, *fout, *fptr;
 
       fptr = bfd_get_filename (abfd);
   if (bfd_get_filename (abfd) != 0)
     {
       /* Strip path and suffix information.  */
       char *fname, *fout, *fptr;
 
       fptr = bfd_get_filename (abfd);
-      fname = alloca (strlen (fptr) + 1);
-      strcpy (fname, fptr);
+      fname = strdup (fptr);
+
+      /* Strip VMS path.  */
       fout = strrchr (fname, ']');
       fout = strrchr (fname, ']');
-      if (fout == 0)
+      if (fout == NULL)
        fout = strchr (fname, ':');
        fout = strchr (fname, ':');
-      if (fout != 0)
+      if (fout != NULL)
        fout++;
       else
        fout = fname;
 
        fout++;
       else
        fout = fname;
 
-      /* Strip .obj suffix.  */
-      fptr = strrchr (fname, '.');
-      if ((fptr != 0)
-         && (strcasecmp (fptr, ".OBJ") == 0))
+      /* Strip UNIX path.  */
+      fptr = strrchr (fout, '/');
+      if (fptr != NULL)
+       fout = fptr + 1;
+
+      /* Strip suffix.  */
+      fptr = strrchr (fout, '.');
+      if (fptr != 0)
        *fptr = 0;
 
        *fptr = 0;
 
+      /* Convert to upper case and truncate at 31 characters.
+         (VMS object file format restricts module name length to 31).  */
       fptr = fout;
       while (*fptr != 0)
        {
          *fptr = TOUPPER (*fptr);
          fptr++;
       fptr = fout;
       while (*fptr != 0)
        {
          *fptr = TOUPPER (*fptr);
          fptr++;
-         if ((*fptr == ';')
-            || ((fptr - fout) > 31))
+         if (*fptr == ';' || (fptr - fout) >= 31)
            *fptr = 0;
        }
       _bfd_vms_output_counted (abfd, fout);
            *fptr = 0;
        }
       _bfd_vms_output_counted (abfd, fout);
+      free (fname);
     }
   else
     _bfd_vms_output_counted (abfd, "NONAME");
 
   _bfd_vms_output_counted (abfd, BFD_VERSION_STRING);
     }
   else
     _bfd_vms_output_counted (abfd, "NONAME");
 
   _bfd_vms_output_counted (abfd, BFD_VERSION_STRING);
-  _bfd_vms_output_dump (abfd, get_vms_time_string (), 17);
-  _bfd_vms_output_fill (abfd, 0, 17);
+  _bfd_vms_output_dump (abfd, get_vms_time_string (), EMH_DATE_LENGTH);
+  _bfd_vms_output_fill (abfd, 0, EMH_DATE_LENGTH);
   _bfd_vms_output_flush (abfd);
 
   /* LMN.  */
   _bfd_vms_output_begin (abfd, EOBJ_S_C_EMH, EMH_S_C_LNM);
   _bfd_vms_output_flush (abfd);
 
   /* LMN.  */
   _bfd_vms_output_begin (abfd, EOBJ_S_C_EMH, EMH_S_C_LNM);
-  _bfd_vms_output_dump (abfd, (unsigned char *) STRING_COMMA_LEN ("GAS proGIS"));
+  snprintf (version, sizeof (version), "GAS BFD v%s", BFD_VERSION_STRING);
+  _bfd_vms_output_dump (abfd, (unsigned char *)version, strlen (version));
   _bfd_vms_output_flush (abfd);
 
   /* SRC.  */
   _bfd_vms_output_flush (abfd);
 
   /* SRC.  */
@@ -319,10 +357,10 @@ _bfd_vms_slurp_eom (bfd *abfd, int objtype)
     }
   else
     {
     }
   else
     {
-      PRIV (eom_data).eom_l_total_lps = bfd_getl32 (vms_rec + 4);
-      PRIV (eom_data).eom_b_comcod = *(vms_rec + 8);
-
-      if (PRIV (eom_data).eom_b_comcod > 1)
+      PRIV (eom_data).eom_l_total_lps
+        = bfd_getl32 (vms_rec + EEOM_S_L_TOTAL_LPS);
+      PRIV (eom_data).eom_w_comcod = bfd_getl16 (vms_rec + EEOM_S_W_COMCOD);
+      if (PRIV (eom_data).eom_w_comcod > 1)
        {
          (*_bfd_error_handler) (_("Object module NOT error-free !\n"));
          bfd_set_error (bfd_error_bad_value);
        {
          (*_bfd_error_handler) (_("Object module NOT error-free !\n"));
          bfd_set_error (bfd_error_bad_value);
@@ -332,9 +370,11 @@ _bfd_vms_slurp_eom (bfd *abfd, int objtype)
       if (PRIV (rec_size) > 10)
        {
           PRIV (eom_data).eom_has_transfer = TRUE;
       if (PRIV (rec_size) > 10)
        {
           PRIV (eom_data).eom_has_transfer = TRUE;
-          PRIV (eom_data).eom_b_tfrflg = *(vms_rec + 9);
-          PRIV (eom_data).eom_l_psindx = bfd_getl32 (vms_rec + 12);
-          PRIV (eom_data).eom_l_tfradr = bfd_getl32 (vms_rec + 16);
+          PRIV (eom_data).eom_b_tfrflg = vms_rec[EEOM_S_B_TFRFLG];
+          PRIV (eom_data).eom_l_psindx
+            = bfd_getl32 (vms_rec + EEOM_S_L_PSINDX);
+          PRIV (eom_data).eom_l_tfradr
+            = bfd_getl32 (vms_rec + EEOM_S_L_TFRADR);
 
           abfd->start_address = PRIV (eom_data).eom_l_tfradr;
        }
 
           abfd->start_address = PRIV (eom_data).eom_l_tfradr;
        }
@@ -376,3 +416,1001 @@ _bfd_vms_write_eom (bfd *abfd, int objtype)
   _bfd_vms_output_end (abfd);
   return 0;
 }
   _bfd_vms_output_end (abfd);
   return 0;
 }
+
+/* Read & process IHD/EIHD record.
+   Return 0 on success, -1 on error  */
+int
+_bfd_vms_slurp_ihd (bfd *abfd, unsigned int *isd_offset,
+                   unsigned int *ihs_offset)
+{
+  unsigned int imgtype, size;
+  bfd_vma symvva;
+
+#if VMS_DEBUG
+  vms_debug (8, "_bfd_vms_slurp_ihd\n");
+#endif
+
+  size = bfd_getl32 (PRIV (vms_rec) + EIHD_S_L_SIZE);
+  imgtype = bfd_getl32 (PRIV (vms_rec) + EIHD_S_L_IMGTYPE);
+
+  if (imgtype == EIHD_S_K_EXE)
+    abfd->flags |= EXEC_P;
+
+  symvva = bfd_getl64 (PRIV (vms_rec) + EIHD_S_Q_SYMVVA);
+  if (symvva != 0)
+    {
+      PRIV (symvva) = symvva;
+      abfd->flags |= DYNAMIC;
+    }
+
+  *isd_offset = bfd_getl32 (PRIV (vms_rec) + EIHD_S_L_ISDOFF);
+  *ihs_offset = bfd_getl32 (PRIV (vms_rec) + EIHD_S_L_SYMDBGOFF);
+
+#if VMS_DEBUG
+  vms_debug (4, "EIHD record size %d imgtype %d symvva 0x%llx isd %d ihs %d\n",
+            size, imgtype, symvva, *isd_offset, *ihs_offset);
+#endif
+
+  return 0;
+}
+
+/* Read & process ISD/EISD record
+   return 0 on success, -1 on error  */
+
+int
+_bfd_vms_slurp_isd (bfd *abfd, unsigned int offset)
+{
+  int section_count = 0;
+  unsigned char *p;
+  unsigned int rec_size;
+
+#if VMS_DEBUG
+  vms_debug (8, "_bfd_vms_slurp_isd\n");
+#endif
+
+  for (p = PRIV (vms_rec) + offset;
+       (rec_size = bfd_getl32 (p + EISD_S_L_EISDSIZE)) != 0;
+       p += rec_size)
+    {
+      unsigned long long vaddr = bfd_getl64 (p + EISD_S_Q_VIR_ADDR);
+      unsigned int size = bfd_getl32 (p + EISD_S_L_SECSIZE);
+      unsigned int flags = bfd_getl32 (p + EISD_S_L_FLAGS);
+      unsigned int vbn = bfd_getl32 (p + EISD_S_L_VBN);
+      char *name = NULL;
+      asection *section;
+      flagword bfd_flags;
+
+#if VMS_DEBUG
+      vms_debug (4, "EISD record at 0x%x size 0x%x addr 0x%x bfd_flags 0x%x block %d\n",
+                p - PRIV (vms_rec), size, vaddr, flags, vbn);
+#endif
+
+      /* VMS combines psects from .obj files into isects in the .exe.  This
+        process doesn't preserve enough information to reliably determine
+        what's in each section without examining the data.  This is
+        especially true of DWARF debug sections.  */
+      bfd_flags = SEC_ALLOC;
+
+      if (flags & EISD_S_M_EXE)
+       bfd_flags |= SEC_CODE | SEC_HAS_CONTENTS | SEC_LOAD;
+
+      if (flags & EISD_S_M_NONSHRADR)
+       bfd_flags |= SEC_DATA | SEC_HAS_CONTENTS | SEC_LOAD;
+
+      if (!(flags & EISD_S_M_WRT))
+       bfd_flags |= SEC_READONLY;
+
+      if (flags & EISD_S_M_DZRO)
+       bfd_flags |= SEC_DATA;
+
+      if (flags & EISD_S_M_FIXUPVEC)
+       bfd_flags |= SEC_DATA | SEC_HAS_CONTENTS | SEC_LOAD;
+
+      if (flags & EISD_S_M_CRF)
+       bfd_flags |= SEC_HAS_CONTENTS | SEC_LOAD;
+
+      if (flags & EISD_S_M_GBL)
+       {
+         name = _bfd_vms_save_counted_string (p + EISD_S_T_GBLNAM);
+         bfd_flags |= SEC_COFF_SHARED_LIBRARY;
+         bfd_flags &= ~(SEC_ALLOC | SEC_LOAD);
+       }
+      else
+       {
+         name = (char*) bfd_alloc (abfd, 32);
+         sprintf (name, "$LOCAL_%03d$", section_count++);
+       }
+
+      section = bfd_make_section (abfd, name);
+
+      if (!section)
+       return -1;
+
+      section->filepos = vbn ? VMS_BLOCK_SIZE * (vbn - 1) : (unsigned int)-1;
+      section->size = size;
+      section->vma = vaddr;
+
+      if (!bfd_set_section_flags (abfd, section, bfd_flags))
+       return -1;
+    }
+
+  return 0;
+}
+
+/* Read & process IHS/EIHS record
+   return 0 on success, -1 on error  */
+int
+_bfd_vms_slurp_ihs (bfd *abfd, unsigned int offset)
+{
+  unsigned char *p = PRIV (vms_rec) + offset;
+  unsigned int gstvbn = bfd_getl32 (p + EIHS_S_L_GSTVBN);
+  unsigned int gstsize ATTRIBUTE_UNUSED = bfd_getl32 (p + EIHS_S_L_GSTSIZE);
+  unsigned int dstvbn = bfd_getl32 (p + EIHS_S_L_DSTVBN);
+  unsigned int dstsize = bfd_getl32 (p + EIHS_S_L_DSTSIZE);
+  unsigned int dmtvbn = bfd_getl32 (p + EIHS_S_L_DMTVBN);
+  unsigned int dmtbytes = bfd_getl32 (p + EIHS_S_L_DMTBYTES);
+  asection *section;
+
+#if VMS_DEBUG
+  vms_debug (8, "_bfd_vms_slurp_ihs\n");
+  vms_debug (4, "EIHS record gstvbn %d gstsize %d dstvbn %d dstsize %d dmtvbn %d dmtbytes %d\n",
+            gstvbn, gstsize, dstvbn, dstsize, dmtvbn, dmtbytes);
+#endif
+
+  if (dstvbn)
+    {
+      flagword bfd_flags = SEC_HAS_CONTENTS | SEC_DEBUGGING;
+
+      section = bfd_make_section (abfd, "$DST$");
+      if (!section)
+       return -1;
+
+      section->size = dstsize;
+      section->filepos = VMS_BLOCK_SIZE * (dstvbn - 1);
+
+      if (!bfd_set_section_flags (abfd, section, bfd_flags))
+       return -1;
+
+      PRIV (dst_section) = section;
+      abfd->flags |= (HAS_DEBUG | HAS_LINENO);
+    }
+
+  if (dmtvbn)
+    {
+      flagword bfd_flags = SEC_HAS_CONTENTS | SEC_DEBUGGING;
+
+      section = bfd_make_section (abfd, "$DMT$");
+      if (!section)
+       return -1;
+
+      section->size = dmtbytes;
+      section->filepos = VMS_BLOCK_SIZE * (dmtvbn - 1);
+
+      if (!bfd_set_section_flags (abfd, section, bfd_flags))
+       return -1;
+    }
+
+  if (gstvbn)
+    {
+      flagword bfd_flags = SEC_HAS_CONTENTS;
+
+      section = bfd_make_section (abfd, "$GST$");
+      if (!section)
+       return -1;
+
+      if (bfd_seek (abfd, VMS_BLOCK_SIZE * (gstvbn - 1), SEEK_SET))
+       {
+         bfd_set_error (bfd_error_file_truncated);
+         return -1;
+       }
+
+      if (_bfd_vms_slurp_object_records (abfd) != 0)
+       return -1;
+
+      section->filepos = VMS_BLOCK_SIZE * (gstvbn - 1);
+      section->size = bfd_tell (abfd) - section->filepos;
+
+      if (!bfd_set_section_flags (abfd, section, bfd_flags))
+       return -1;
+
+      abfd->flags |= HAS_SYMS;
+    }
+
+  return 0;
+}
+
+/* Build a new module for the specified BFD.  */
+
+static struct module *
+new_module (bfd *abfd)
+{
+  struct module *module
+    = (struct module *) bfd_zalloc (abfd, sizeof (struct module));
+  module->file_table_count = 16; /* Arbitrary.  */
+  module->file_table
+    = bfd_malloc (module->file_table_count * sizeof (struct fileinfo));
+  return module;
+}
+
+/* Parse debug info for a module and internalize it.  */
+
+static void
+parse_module (bfd *abfd, struct module *module, unsigned char *ptr,
+             unsigned int length)
+{
+  unsigned char *maxptr = ptr + length, *src_ptr, *pcl_ptr;
+  unsigned int prev_linum = 0, curr_linenum = 0;
+  bfd_vma prev_pc = 0, curr_pc = 0;
+  struct srecinfo *curr_srec, *srec;
+  struct lineinfo *curr_line, *line;
+  struct funcinfo *funcinfo;
+
+  /* Initialize tables with zero element.  */
+  curr_srec = (struct srecinfo *) bfd_zalloc (abfd, sizeof (struct srecinfo));
+  module->srec_table = curr_srec;
+
+  curr_line = (struct lineinfo *) bfd_zalloc (abfd, sizeof (struct lineinfo));
+  module->line_table = curr_line;
+
+  while (ptr < maxptr)
+    {
+      /* The first byte is not counted in the recorded length.  */
+      int rec_length = bfd_getl16 (ptr) + 1;
+      int rec_type = bfd_getl16 (ptr + 2);
+
+#if VMS_DEBUG
+      _bfd_vms_debug (2, "DST record: length %d, type %d\n",
+                     rec_length, rec_type);
+#endif
+
+      switch (rec_type)
+       {
+       case DST_S_C_MODBEG:
+         module->name
+           = _bfd_vms_save_counted_string (ptr + DST_S_B_MODBEG_NAME);
+
+         curr_pc = 0;
+         prev_pc = 0;
+         curr_linenum = 0;
+         prev_linum = 0;
+
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "module: %s\n", module->name);
+#endif
+         break;
+
+       case DST_S_C_MODEND:
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "end module\n");
+#endif
+         break;
+
+       case DST_S_C_RTNBEG:
+         funcinfo = (struct funcinfo *)
+           bfd_zalloc (abfd, sizeof (struct funcinfo));
+          funcinfo->name
+           = _bfd_vms_save_counted_string (ptr + DST_S_B_RTNBEG_NAME);
+         funcinfo->low = bfd_getl32 (ptr + DST_S_L_RTNBEG_ADDRESS);
+         funcinfo->next = module->func_table;
+         module->func_table = funcinfo;
+    
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "routine: %s at 0x%x\n",
+                         funcinfo->name, funcinfo->low);
+#endif
+         break;
+
+       case DST_S_C_RTNEND:
+         module->func_table->high = module->func_table->low
+           + bfd_getl32 (ptr + DST_S_L_RTNEND_SIZE) - 1;
+
+         if (module->func_table->high > module->high)
+           module->high = module->func_table->high;
+
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "end routine\n");
+#endif
+         break;
+
+       case DST_S_C_PROLOG:
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "prologue\n");
+#endif
+         break;
+
+       case DST_S_C_EPILOG:
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "epilog\n");
+#endif
+         break;
+
+       case DST_S_C_BLKBEG:
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "block\n");
+#endif
+         break;
+
+       case DST_S_C_BLKEND:
+#if VMS_DEBUG
+          _bfd_vms_debug (3, "end block\n");
+#endif
+         break;
+
+       case DST_S_C_SOURCE:
+         src_ptr = ptr + DST_S_C_SOURCE_HEADER_SIZE;
+
+#if VMS_DEBUG
+         _bfd_vms_debug (3, "source info\n");
+#endif
+
+         while (src_ptr < ptr + rec_length)
+           {
+             int cmd = src_ptr[0], cmd_length, data;
+
+             switch (cmd)
+               {
+               case DST_S_C_SRC_DECLFILE:
+                 {
+                   unsigned int fileid
+                     = bfd_getl16 (src_ptr + DST_S_W_SRC_DF_FILEID);
+                   char *filename
+                     = _bfd_vms_save_counted_string (src_ptr
+                         + DST_S_B_SRC_DF_FILENAME);
+
+                   while (fileid >= module->file_table_count)
+                     {
+                       module->file_table_count *= 2;
+                       module->file_table
+                         = bfd_realloc (module->file_table,
+                                        module->file_table_count
+                                          * sizeof (struct fileinfo));
+                     }
+
+                   module->file_table [fileid].name = filename;
+                   module->file_table [fileid].srec = 1;
+                   cmd_length = src_ptr[DST_S_B_SRC_DF_LENGTH] + 2;
+#if VMS_DEBUG
+                   _bfd_vms_debug (4, "DST_S_C_SRC_DECLFILE: %d, %s\n",
+                                   fileid,
+                                   module->file_table [fileid].name);
+#endif
+                 }
+                 break;
+
+               case DST_S_C_SRC_DEFLINES_B:
+                 /* Perform the association and set the next higher index
+                    to the limit.  */
+                 data = src_ptr[DST_S_B_SRC_UNSBYTE];
+                 srec = (struct srecinfo *)
+                   bfd_zalloc (abfd, sizeof (struct srecinfo));
+                 srec->line = curr_srec->line + data;
+                 srec->srec = curr_srec->srec + data;
+                 srec->sfile = curr_srec->sfile;
+                 curr_srec->next = srec;
+                 curr_srec = srec;
+                 cmd_length = 2;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_DEFLINES_B: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_DEFLINES_W:
+                 /* Perform the association and set the next higher index
+                    to the limit.  */
+                 data = bfd_getl16 (src_ptr + DST_S_W_SRC_UNSWORD);
+                 srec = (struct srecinfo *)
+                   bfd_zalloc (abfd, sizeof (struct srecinfo));
+                 srec->line = curr_srec->line + data;
+                 srec->srec = curr_srec->srec + data,
+                 srec->sfile = curr_srec->sfile;
+                 curr_srec->next = srec;
+                 curr_srec = srec;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_DEFLINES_W: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_INCRLNUM_B:
+                 data = src_ptr[DST_S_B_SRC_UNSBYTE];
+                 curr_srec->line += data;
+                 cmd_length = 2;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_INCRLNUM_B: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_SETFILE:
+                 data = bfd_getl16 (src_ptr + DST_S_W_SRC_UNSWORD);
+                 curr_srec->sfile = data;
+                 curr_srec->srec = module->file_table[data].srec;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_SETFILE: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_SETLNUM_L:
+                 data = bfd_getl32 (src_ptr + DST_S_L_SRC_UNSLONG);
+                 curr_srec->line = data;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_SETLNUM_L: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_SETLNUM_W:
+                 data = bfd_getl16 (src_ptr + DST_S_W_SRC_UNSWORD);
+                 curr_srec->line = data;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_SETLNUM_W: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_SETREC_L:
+                 data = bfd_getl32 (src_ptr + DST_S_L_SRC_UNSLONG);
+                 curr_srec->srec = data;
+                 module->file_table[curr_srec->sfile].srec = data;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_SETREC_L: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_SETREC_W:
+                 data = bfd_getl16 (src_ptr + DST_S_W_SRC_UNSWORD);
+                 curr_srec->srec = data;
+                 module->file_table[curr_srec->sfile].srec = data;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_SETREC_W: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SRC_FORMFEED:
+                 cmd_length = 1;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SRC_FORMFEED\n");
+#endif
+                 break;
+
+               default:
+                 (*_bfd_error_handler) (_("unknown source command %d"),
+                                        cmd);
+                 cmd_length = 2;
+                 break;
+               }
+
+             src_ptr += cmd_length;
+           }
+         break;
+
+       case DST_S_C_LINE_NUM:
+         pcl_ptr = ptr + DST_S_C_LINE_NUM_HEADER_SIZE;
+
+#if VMS_DEBUG
+         _bfd_vms_debug (3, "line info\n");
+#endif
+
+         while (pcl_ptr < ptr + rec_length)
+           {
+             /* The command byte is signed so we must sign-extend it.  */
+             int cmd = ((signed char *)pcl_ptr)[0], cmd_length, data;
+
+             switch (cmd)
+               {
+               case DST_S_C_DELTA_PC_W:
+                 data = bfd_getl16 (pcl_ptr + DST_S_W_PCLINE_UNSWORD);
+                 curr_pc += data;
+                 curr_linenum += 1;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_DELTA_PC_W: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_DELTA_PC_L:
+                 data = bfd_getl32 (pcl_ptr + DST_S_L_PCLINE_UNSLONG);
+                 curr_pc += data;
+                 curr_linenum += 1;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_DELTA_PC_L: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_INCR_LINUM:
+                 data = pcl_ptr[DST_S_B_PCLINE_UNSBYTE];
+                 curr_linenum += data;
+                 cmd_length = 2;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_INCR_LINUM: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_INCR_LINUM_W:
+                 data = bfd_getl16 (pcl_ptr + DST_S_W_PCLINE_UNSWORD);
+                 curr_linenum += data;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_INCR_LINUM_W: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_INCR_LINUM_L:
+                 data = bfd_getl32 (pcl_ptr + DST_S_L_PCLINE_UNSLONG);
+                 curr_linenum += data;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_INCR_LINUM_L: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SET_LINUM_INCR:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_SET_LINUM_INCR not implemented"));
+                 cmd_length = 2;
+                 break;
+
+               case DST_S_C_SET_LINUM_INCR_W:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_SET_LINUM_INCR_W not implemented"));
+                 cmd_length = 3;
+                 break;
+
+               case DST_S_C_RESET_LINUM_INCR:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_RESET_LINUM_INCR not implemented"));
+                 cmd_length = 1;
+                 break;
+
+               case DST_S_C_BEG_STMT_MODE:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_BEG_STMT_MODE not implemented"));
+                 cmd_length = 1;
+                 break;
+
+               case DST_S_C_END_STMT_MODE:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_END_STMT_MODE not implemented"));
+                 cmd_length = 1;
+                 break;
+
+               case DST_S_C_SET_LINUM_B:
+                 data = pcl_ptr[DST_S_B_PCLINE_UNSBYTE];
+                 curr_linenum = data;
+                 cmd_length = 2;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SET_LINUM_B: %d\n", data);
+#endif
+                 break;
+       
+               case DST_S_C_SET_LINE_NUM:
+                 data = bfd_getl16 (pcl_ptr + DST_S_W_PCLINE_UNSWORD);
+                 curr_linenum = data;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SET_LINE_NUM: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SET_LINUM_L:
+                 data = bfd_getl32 (pcl_ptr + DST_S_L_PCLINE_UNSLONG);
+                 curr_linenum = data;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SET_LINUM_L: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SET_PC:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_SET_PC not implemented"));
+                 cmd_length = 2;
+                 break;
+
+               case DST_S_C_SET_PC_W:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_SET_PC_W not implemented"));
+                 cmd_length = 3;
+                 break;
+
+               case DST_S_C_SET_PC_L:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_SET_PC_L not implemented"));
+                 cmd_length = 5;
+                 break;
+
+               case DST_S_C_SET_STMTNUM:
+                 (*_bfd_error_handler)
+                   (_("DST_S_C_SET_STMTNUM not implemented"));
+                 cmd_length = 2;
+                 break;
+
+               case DST_S_C_TERM:
+                 data = pcl_ptr[DST_S_B_PCLINE_UNSBYTE];
+                 curr_pc += data;
+                 cmd_length = 2;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_TERM: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_TERM_W:
+                 data = bfd_getl16 (pcl_ptr + DST_S_W_PCLINE_UNSWORD);
+                 curr_pc += data;
+                 cmd_length = 3;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_TERM_W: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_TERM_L:
+                 data = bfd_getl32 (pcl_ptr + DST_S_L_PCLINE_UNSLONG);
+                 curr_pc += data;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_TERM_L: %d\n", data);
+#endif
+                 break;
+
+               case DST_S_C_SET_ABS_PC:
+                 data = bfd_getl32 (pcl_ptr + DST_S_L_PCLINE_UNSLONG);
+                 curr_pc = data;
+                 cmd_length = 5;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "DST_S_C_SET_ABS_PC: 0x%x\n", data);
+#endif
+                 break;
+
+               default:
+                 if (cmd <= 0)
+                   {
+                     curr_pc -= cmd;
+                     curr_linenum += 1;
+                     cmd_length = 1;
+#if VMS_DEBUG
+                     _bfd_vms_debug (4, "bump pc to 0x%llx and line to %d\n",
+                                     curr_pc, curr_linenum);
+#endif
+                   }
+                 else
+                   {
+                     (*_bfd_error_handler) (_("unknown line command %d"),
+                                            cmd);
+                     cmd_length = 2;
+                   }
+                 break;
+               }
+
+             if ((curr_linenum != prev_linum && curr_pc != prev_pc)
+                 || cmd <= 0
+                 || cmd == DST_S_C_DELTA_PC_L
+                 || cmd == DST_S_C_DELTA_PC_W)
+               {
+                 line = (struct lineinfo *)
+                   bfd_zalloc (abfd, sizeof (struct lineinfo));
+                 line->address = curr_pc;
+                 line->line = curr_linenum;
+
+                 curr_line->next = line;
+                 curr_line = line;
+
+                 prev_linum = curr_linenum;
+                 prev_pc = curr_pc;
+#if VMS_DEBUG
+                 _bfd_vms_debug (4, "-> correlate pc 0x%llx with line %d\n",
+                                 curr_pc, curr_linenum);
+#endif
+               }
+
+             pcl_ptr += cmd_length;
+           }
+         break;
+
+       case 0x17: /* Undocumented type used by DEC C to declare equates.  */
+#if VMS_DEBUG
+         _bfd_vms_debug (3, "undocumented type 0x17\n");
+#endif
+         break;
+
+       default:
+#if VMS_DEBUG
+         _bfd_vms_debug (3, "ignoring record\n");
+#endif
+         break;
+
+       }
+
+      ptr += rec_length;
+    }
+
+  /* Finalize tables with EOL marker.  */
+  srec = (struct srecinfo *) bfd_zalloc (abfd, sizeof (struct srecinfo));
+  srec->line = (unsigned int) -1;
+  srec->srec = (unsigned int) -1;
+  curr_srec->next = srec;
+
+  line = (struct lineinfo *) bfd_zalloc (abfd, sizeof (struct lineinfo));
+  line->line = (unsigned int) -1;
+  line->address = (bfd_vma) -1;
+  curr_line->next = line;
+
+  /* Advertise that this module has been parsed.  This is needed
+     because parsing can be either performed at module creation
+     or deferred until debug info is consumed.  */
+  SET_MODULE_PARSED (module);
+}
+
+/* Build the list of modules for the specified BFD.  */
+
+static struct module *
+build_module_list (bfd *abfd)
+{
+  struct module *module, *list = NULL;
+  asection *dmt;
+
+  if ((dmt = bfd_get_section_by_name (abfd, "$DMT$")))
+    {
+      /* We have a DMT section so this must be an image.  Parse the
+        section and build the list of modules.  This is sufficient
+        since we can compute the start address and the end address
+        of every module from the section contents.  */
+      bfd_size_type size = bfd_get_section_size (dmt);
+      unsigned char *ptr, *end;
+
+      ptr = (unsigned char *) bfd_alloc (abfd, size);
+      if (! ptr)
+       return NULL;
+
+      if (! bfd_get_section_contents (abfd, dmt, ptr, 0, size))
+       return NULL;
+
+#if VMS_DEBUG
+      _bfd_vms_debug (2, "DMT\n");
+#endif
+
+      end = ptr + size;
+
+      while (ptr < end)
+       {
+         /* Each header declares a module with its start offset and size
+            of debug info in the DST section, as well as the count of
+            program sections (i.e. address spans) it contains.  */
+         int modbeg = bfd_getl32 (ptr + DBG_S_L_DMT_MODBEG);
+         int size = bfd_getl32 (ptr + DBG_S_L_DST_SIZE);
+         int count = bfd_getl16 (ptr + DBG_S_W_DMT_PSECT_COUNT);
+         ptr += DBG_S_C_DMT_HEADER_SIZE;
+
+#if VMS_DEBUG
+         _bfd_vms_debug (3, "module: modbeg = %d, size = %d, count = %d\n",
+                         modbeg, size, count);
+#endif
+
+         /* We create a 'module' structure for each program section since
+            we only support contiguous addresses in a 'module' structure.
+            As a consequence, the actual debug info in the DST section is
+            shared and can be parsed multiple times; that doesn't seem to
+            cause problems in practice.  */
+         while (count-- > 0)
+           {
+             int start = bfd_getl32 (ptr + DBG_S_L_DMT_PSECT_START);
+             int length = bfd_getl32 (ptr + DBG_S_L_DMT_PSECT_LENGTH);
+             module = new_module (abfd);
+             module->modbeg = modbeg;
+             module->size = size;
+             module->low = start;
+             module->high = start + length;
+             module->next = list;
+             list = module;
+             ptr += DBG_S_C_DMT_PSECT_SIZE;
+
+#if VMS_DEBUG
+             _bfd_vms_debug (4, "section: start = 0x%x, length = %d\n",
+                             start, length);
+#endif
+           }
+       }
+    }
+  else
+    {
+      /* We don't have a DMT section so this must be an object.  Parse
+        the module right now in order to compute its start address and
+        end address.  */
+      module = new_module (abfd);
+      parse_module (abfd, module, PRIV (dst_section)->contents,
+                   PRIV (dst_ptr_end) - PRIV (dst_section)->contents);
+      list = module;
+    }
+
+  return list;
+}
+
+/* Calculate and return the name of the source file and the line nearest
+   to the wanted location in the specified module.  */
+
+static bfd_boolean
+module_find_nearest_line (bfd *abfd, struct module *module, bfd_vma addr,
+                         const char **file, const char **func, 
+                         unsigned int *line)
+{
+  struct funcinfo *funcinfo;
+  struct lineinfo *lineinfo;
+  struct srecinfo *srecinfo;
+  bfd_boolean ret = FALSE;
+
+  /* Parse this module if that was not done at module creation.  */
+  if (! IS_MODULE_PARSED (module))
+    {
+      unsigned int size = module->size;
+      unsigned int modbeg = PRIV (dst_section)->filepos + module->modbeg;
+      unsigned char *buffer = (unsigned char *) bfd_malloc (module->size);
+
+      if (bfd_seek (abfd, modbeg, SEEK_SET) != 0
+         || bfd_bread (buffer, size, abfd) != size)
+       {
+         bfd_set_error (bfd_error_no_debug_section);
+         return FALSE;
+       }
+
+      parse_module (abfd, module, buffer, size);
+      free (buffer);
+    }
+
+  /* Find out the function (if any) that contains the address.  */
+  for (funcinfo = module->func_table; funcinfo; funcinfo = funcinfo->next)
+    if (addr >= funcinfo->low && addr <= funcinfo->high)
+      {
+        *func = funcinfo->name;
+       ret = TRUE;
+       break;
+      }
+
+  /* Find out the source file and the line nearest to the address.  */
+  for (lineinfo = module->line_table; lineinfo; lineinfo = lineinfo->next)
+    if (lineinfo->next && addr < lineinfo->next->address)
+      {
+       for (srecinfo = module->srec_table; srecinfo; srecinfo = srecinfo->next)
+         if (srecinfo->next && lineinfo->line < srecinfo->next->line)
+           {
+             if (srecinfo->sfile > 0)
+               {
+                 *file = module->file_table[srecinfo->sfile].name;
+                 *line = srecinfo->srec + lineinfo->line - srecinfo->line;
+               }
+             else
+               {
+                 *file = module->name;
+                 *line = lineinfo->line;
+               }
+             return TRUE;
+           }
+
+       break;
+      }
+
+  return ret;
+}
+
+/* Provided a BFD, a section and an offset into the section, calculate and
+   return the name of the source file and the line nearest to the wanted
+   location.  */
+
+bfd_boolean
+_bfd_vms_find_nearest_dst_line (bfd *abfd, asection *section,
+                               asymbol **symbols ATTRIBUTE_UNUSED,
+                               bfd_vma offset, const char **file,
+                               const char **func, unsigned int *line)
+{
+  struct module *module;
+
+  /* What address are we looking for?  */
+  bfd_vma addr = section->vma + offset;
+
+  *file = NULL;
+  *func = NULL;
+  *line = 0;
+
+  if (PRIV (dst_section) == NULL)
+    return FALSE;
+
+  if (PRIV (modules) == NULL)
+    {
+      PRIV (modules) = build_module_list (abfd);
+      if (PRIV (modules) == NULL)
+        return FALSE;
+    }
+
+  for (module = PRIV (modules); module; module = module->next)
+    if (addr >= module->low && addr <= module->high)
+      return module_find_nearest_line (abfd, module, addr, file, func, line);
+
+  return FALSE;
+}
+
+/* Process EDBG/ETBT record.
+   Return 0 on success, -1 on error  */
+
+static int
+vms_slurp_debug (bfd *abfd)
+{
+  if (PRIV (dst_section) == NULL)
+    {
+      /* We have no way to find out beforehand how much debug info there
+        is in an object file, so pick an initial amount and grow it as
+        needed later.  */
+      flagword flags = SEC_HAS_CONTENTS | SEC_DEBUGGING | SEC_RELOC;
+      asection *section = bfd_make_section (abfd, "$DST$");
+      if (!section)
+       return -1;
+      section->size = 1024;
+      if (!bfd_set_section_flags (abfd, section, flags))
+       return -1;
+      section->contents = ((unsigned char *)
+                          bfd_zmalloc (section->size));
+      if (section->contents == NULL)
+       return -1;
+      section->filepos = (unsigned int)-1;
+      PRIV (dst_section) = section;
+    }
+
+  PRIV (image_section) = PRIV (dst_section);
+  PRIV (image_ptr) = PRIV (dst_section)->contents;
+  return _bfd_vms_slurp_tir (abfd, EOBJ_S_C_ETIR);
+}
+
+/* Process DBG/EDBG record.
+   Return 0 on success, -1 on error.  */
+
+int
+_bfd_vms_slurp_dbg (bfd *abfd, int objtype ATTRIBUTE_UNUSED)
+{
+#if VMS_DEBUG
+  _bfd_vms_debug (2, "DBG/EDBG\n");
+#endif
+
+  abfd->flags |= (HAS_DEBUG | HAS_LINENO);
+
+  return vms_slurp_debug (abfd);
+}
+
+/* Process TBT/ETBT record.
+   Return 0 on success, -1 on error.  */
+
+int
+_bfd_vms_slurp_tbt (bfd *abfd, int objtype ATTRIBUTE_UNUSED)
+{
+#if VMS_DEBUG
+  _bfd_vms_debug (2, "TBT/ETBT\n");
+#endif
+
+  abfd->flags |= HAS_LINENO;
+
+  return vms_slurp_debug (abfd);
+}
+
+/* Write DBG/EDBG record.  */
+
+int
+_bfd_vms_write_dbg (bfd *abfd ATTRIBUTE_UNUSED, int objtype ATTRIBUTE_UNUSED)
+{
+#if VMS_DEBUG
+  _bfd_vms_debug (2, "vms_write_dbg (%p, objtype)\n", abfd, objtype);
+#endif
+
+  return 0;
+}
+
+/* Write TBT/ETBT record.  */
+
+int
+_bfd_vms_write_tbt (bfd *abfd ATTRIBUTE_UNUSED, int objtype ATTRIBUTE_UNUSED)
+{
+#if VMS_DEBUG
+  _bfd_vms_debug (2, "vms_write_tbt (%p, %d)\n", abfd, objtype);
+#endif
+
+  return 0;
+}