/* 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 <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. */
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:
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:
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);
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. */
}
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);
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;
}
_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;
+}