]> oss.titaniummirror.com Git - msp430-binutils.git/blobdiff - bfd/vms-hdr.c
Merge commit 'upstream/2.20'
[msp430-binutils.git] / bfd / vms-hdr.c
index 8e34beef3a4c91560155ec6c614d2570b41631d9..8c10df6222fb1344878b2049fd0dffe4cad657ee 100644 (file)
@@ -1,14 +1,31 @@
 /* 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
-   and
+
    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
 
 #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.  */
@@ -85,15 +112,15 @@ _bfd_vms_slurp_hdr (bfd *abfd, int objtype)
       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:
-      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:
-      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:
@@ -110,15 +137,15 @@ _bfd_vms_slurp_hdr (bfd *abfd, int objtype)
       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:
-      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:
-      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:
@@ -189,6 +216,7 @@ _bfd_vms_write_hdr (bfd *abfd, int objtype)
   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);
@@ -212,44 +240,52 @@ _bfd_vms_write_hdr (bfd *abfd, int objtype)
       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, ']');
-      if (fout == 0)
+      if (fout == NULL)
        fout = strchr (fname, ':');
-      if (fout != 0)
+      if (fout != NULL)
        fout++;
       else
        fout = fname;
 
+      /* Strip UNIX path.  */
+      fptr = strrchr (fout, '/');
+      if (fptr != NULL)
+       fout = fptr + 1;
+
       /* Strip .obj suffix.  */
-      fptr = strrchr (fname, '.');
-      if ((fptr != 0)
-         && (strcasecmp (fptr, ".OBJ") == 0))
+      fptr = strrchr (fout, '.');
+      if (fptr != 0 && strcasecmp (fptr, ".OBJ") == 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++;
-         if ((*fptr == ';')
-            || ((fptr - fout) > 31))
+         if (*fptr == ';' || (fptr - fout) >= 31)
            *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);
-  _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_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.  */
@@ -320,10 +356,10 @@ _bfd_vms_slurp_eom (bfd *abfd, int objtype)
     }
   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);
@@ -333,9 +369,11 @@ _bfd_vms_slurp_eom (bfd *abfd, int objtype)
       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;
        }
@@ -377,3 +415,1001 @@ _bfd_vms_write_eom (bfd *abfd, int objtype)
   _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;
+}