blob: b00ab2a54df11305c068dcb950dfcf5e3f1b1424 [file] [log] [blame]
/* vms.c -- BFD back-end for EVAX (openVMS/Alpha) files.
Copyright (C) 1996-2016 Free Software Foundation, Inc.
Initial version written by Klaus Kaempf (kkaempf@rmi.de)
Major rewrite by Adacore.
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 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
/* TODO:
o overlayed sections
o PIC
o Generation of shared image
o Relocation optimizations
o EISD for the stack
o Vectors isect
o 64 bits sections
o Entry point
o LIB$INITIALIZE
o protected sections (for messages)
...
*/
#include "sysdep.h"
#include "bfd.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "bfdver.h"
#include "vms.h"
#include "vms/eihd.h"
#include "vms/eiha.h"
#include "vms/eihi.h"
#include "vms/eihs.h"
#include "vms/eisd.h"
#include "vms/dmt.h"
#include "vms/dst.h"
#include "vms/eihvn.h"
#include "vms/eobjrec.h"
#include "vms/egsd.h"
#include "vms/egps.h"
#include "vms/esgps.h"
#include "vms/eeom.h"
#include "vms/emh.h"
#include "vms/eiaf.h"
#include "vms/shl.h"
#include "vms/eicp.h"
#include "vms/etir.h"
#include "vms/egsy.h"
#include "vms/esdf.h"
#include "vms/esdfm.h"
#include "vms/esdfv.h"
#include "vms/esrf.h"
#include "vms/egst.h"
#include "vms/eidc.h"
#include "vms/dsc.h"
#include "vms/prt.h"
#include "vms/internal.h"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* The r_type field in a reloc is one of the following values. */
#define ALPHA_R_IGNORE 0
#define ALPHA_R_REFQUAD 1
#define ALPHA_R_BRADDR 2
#define ALPHA_R_HINT 3
#define ALPHA_R_SREL16 4
#define ALPHA_R_SREL32 5
#define ALPHA_R_SREL64 6
#define ALPHA_R_OP_PUSH 7
#define ALPHA_R_OP_STORE 8
#define ALPHA_R_OP_PSUB 9
#define ALPHA_R_OP_PRSHIFT 10
#define ALPHA_R_LINKAGE 11
#define ALPHA_R_REFLONG 12
#define ALPHA_R_CODEADDR 13
#define ALPHA_R_NOP 14
#define ALPHA_R_BSR 15
#define ALPHA_R_LDA 16
#define ALPHA_R_BOH 17
/* These are used with DST_S_C_LINE_NUM. */
#define DST_S_C_LINE_NUM_HEADER_SIZE 4
/* These are used with DST_S_C_SOURCE */
#define DST_S_B_PCLINE_UNSBYTE 1
#define DST_S_W_PCLINE_UNSWORD 1
#define DST_S_L_PCLINE_UNSLONG 1
#define DST_S_B_MODBEG_NAME 14
#define DST_S_L_RTNBEG_ADDRESS 5
#define DST_S_B_RTNBEG_NAME 13
#define DST_S_L_RTNEND_SIZE 5
/* These are used with DST_S_C_SOURCE. */
#define DST_S_C_SOURCE_HEADER_SIZE 4
#define DST_S_B_SRC_DF_LENGTH 1
#define DST_S_W_SRC_DF_FILEID 3
#define DST_S_B_SRC_DF_FILENAME 20
#define DST_S_B_SRC_UNSBYTE 1
#define DST_S_W_SRC_UNSWORD 1
#define DST_S_L_SRC_UNSLONG 1
/* Debugger symbol definitions. */
#define DBG_S_L_DMT_MODBEG 0
#define DBG_S_L_DST_SIZE 4
#define DBG_S_W_DMT_PSECT_COUNT 8
#define DBG_S_C_DMT_HEADER_SIZE 12
#define DBG_S_L_DMT_PSECT_START 0
#define DBG_S_L_DMT_PSECT_LENGTH 4
#define DBG_S_C_DMT_PSECT_SIZE 8
/* VMS module header. */
struct hdr_struct
{
char hdr_b_strlvl;
int hdr_l_arch1;
int hdr_l_arch2;
int hdr_l_recsiz;
char *hdr_t_name;
char *hdr_t_version;
char *hdr_t_date;
char *hdr_c_lnm;
char *hdr_c_src;
char *hdr_c_ttl;
};
#define EMH_DATE_LENGTH 17
/* VMS End-Of-Module records (EOM/EEOM). */
struct eom_struct
{
unsigned int eom_l_total_lps;
unsigned short eom_w_comcod;
bfd_boolean eom_has_transfer;
unsigned char eom_b_tfrflg;
unsigned int eom_l_psindx;
unsigned int eom_l_tfradr;
};
struct vms_symbol_entry
{
bfd *owner;
/* Common fields. */
unsigned char typ;
unsigned char data_type;
unsigned short flags;
/* Section and offset/value of the symbol. */
unsigned int value;
asection *section;
/* Section and offset/value for the entry point (only for subprg). */
asection *code_section;
unsigned int code_value;
/* Symbol vector offset. */
unsigned int symbol_vector;
/* Length of the name. */
unsigned char namelen;
char name[1];
};
/* Stack value for push/pop commands. */
struct stack_struct
{
bfd_vma value;
unsigned int reloc;
};
#define STACKSIZE 128
/* A minimal decoding of DST compilation units. We only decode
what's needed to get to the line number information. */
struct fileinfo
{
char *name;
unsigned int srec;
};
struct srecinfo
{
struct srecinfo *next;
unsigned int line;
unsigned int sfile;
unsigned int srec;
};
struct lineinfo
{
struct lineinfo *next;
bfd_vma address;
unsigned int line;
};
struct funcinfo
{
struct funcinfo *next;
char *name;
bfd_vma low;
bfd_vma high;
};
struct module
{
/* Chain the previously read compilation unit. */
struct module *next;
/* The module name. */
char *name;
/* The start offset and size of debug info in the DST section. */
unsigned int modbeg;
unsigned int size;
/* The lowest and highest addresses contained in this compilation
unit as specified in the compilation unit header. */
bfd_vma low;
bfd_vma high;
/* The listing line table. */
struct lineinfo *line_table;
/* The source record table. */
struct srecinfo *srec_table;
/* A list of the functions found in this module. */
struct funcinfo *func_table;
/* Current allocation of file_table. */
unsigned int file_table_count;
/* An array of the files making up this module. */
struct fileinfo *file_table;
};
/* BFD private data for alpha-vms. */
struct vms_private_data_struct
{
/* If true, relocs have been read. */
bfd_boolean reloc_done;
/* Record input buffer. */
struct vms_rec_rd recrd;
struct vms_rec_wr recwr;
struct hdr_struct hdr_data; /* data from HDR/EMH record */
struct eom_struct eom_data; /* data from EOM/EEOM record */
/* Transfer addresses (entry points). */
bfd_vma transfer_address[4];
/* Array of GSD sections to get the correspond BFD one. */
unsigned int section_max; /* Size of the sections array. */
unsigned int section_count; /* Number of GSD sections. */
asection **sections;
/* Array of raw symbols. */
struct vms_symbol_entry **syms;
/* Canonicalized symbols. */
asymbol **csymbols;
/* Number of symbols. */
unsigned int gsd_sym_count;
/* Size of the syms array. */
unsigned int max_sym_count;
/* Number of procedure symbols. */
unsigned int norm_sym_count;
/* Stack used to evaluate TIR/ETIR commands. */
struct stack_struct *stack;
int stackptr;
/* Content reading. */
asection *image_section; /* section for image_ptr */
file_ptr image_offset; /* Offset for image_ptr. */
struct module *modules; /* list of all compilation units */
/* The DST section. */
asection *dst_section;
unsigned int dst_ptr_offsets_count; /* # of offsets in following array */
unsigned int *dst_ptr_offsets; /* array of saved image_ptr offsets */
/* Shared library support */
bfd_vma symvva; /* relative virtual address of symbol vector */
unsigned int ident;
unsigned char matchctl;
/* Shared library index. This is used for input bfd while linking. */
unsigned int shr_index;
/* Used to place structures in the file. */
file_ptr file_pos;
/* Simply linked list of eisd. */
struct vms_internal_eisd_map *eisd_head;
struct vms_internal_eisd_map *eisd_tail;
/* Simply linked list of eisd for shared libraries. */
struct vms_internal_eisd_map *gbl_eisd_head;
struct vms_internal_eisd_map *gbl_eisd_tail;
/* linkage index counter used by conditional store commands */
unsigned int vms_linkage_index;
};
#define PRIV2(abfd, name) \
(((struct vms_private_data_struct *)(abfd)->tdata.any)->name)
#define PRIV(name) PRIV2(abfd,name)
/* Used to keep extra VMS specific information for a given section.
reloc_size holds the size of the relocation stream, note this
is very different from the number of relocations as VMS relocations
are variable length.
reloc_stream is the actual stream of relocation entries. */
struct vms_section_data_struct
{
/* Maximnum number of entries in sec->relocation. */
unsigned reloc_max;
/* Corresponding eisd. Used only while generating executables. */
struct vms_internal_eisd_map *eisd;
/* PSC flags to be clear. */
flagword no_flags;
/* PSC flags to be set. */
flagword flags;
};
#define vms_section_data(sec) \
((struct vms_section_data_struct *)sec->used_by_bfd)
/* To be called from the debugger. */
struct vms_private_data_struct *bfd_vms_get_data (bfd *);
static int vms_get_remaining_object_record (bfd *, unsigned int);
static bfd_boolean _bfd_vms_slurp_object_records (bfd * abfd);
static void alpha_vms_add_fixup_lp (struct bfd_link_info *, bfd *, bfd *);
static void alpha_vms_add_fixup_ca (struct bfd_link_info *, bfd *, bfd *);
static void alpha_vms_add_fixup_qr (struct bfd_link_info *, bfd *, bfd *,
bfd_vma);
static void alpha_vms_add_fixup_lr (struct bfd_link_info *, unsigned int,
bfd_vma);
static void alpha_vms_add_lw_reloc (struct bfd_link_info *);
static void alpha_vms_add_qw_reloc (struct bfd_link_info *);
struct vector_type
{
unsigned int max_el;
unsigned int nbr_el;
void *els;
};
/* Number of elements in VEC. */
#define VEC_COUNT(VEC) ((VEC).nbr_el)
/* Get the address of the Nth element. */
#define VEC_EL(VEC, TYPE, N) (((TYPE *)((VEC).els))[N])
#define VEC_INIT(VEC) \
do { \
(VEC).max_el = 0; \
(VEC).nbr_el = 0; \
(VEC).els = NULL; \
} while (0)
/* Be sure there is room for a new element. */
static void vector_grow1 (struct vector_type *vec, size_t elsz);
/* Allocate room for a new element and return its address. */
#define VEC_APPEND(VEC, TYPE) \
(vector_grow1 (&VEC, sizeof (TYPE)), &VEC_EL(VEC, TYPE, (VEC).nbr_el++))
/* Append an element. */
#define VEC_APPEND_EL(VEC, TYPE, EL) \
(*(VEC_APPEND (VEC, TYPE)) = EL)
struct alpha_vms_vma_ref
{
bfd_vma vma; /* Vma in the output. */
bfd_vma ref; /* Reference in the input. */
};
struct alpha_vms_shlib_el
{
bfd *abfd;
bfd_boolean has_fixups;
struct vector_type lp; /* Vector of bfd_vma. */
struct vector_type ca; /* Vector of bfd_vma. */
struct vector_type qr; /* Vector of struct alpha_vms_vma_ref. */
};
/* Alpha VMS linker hash table. */
struct alpha_vms_link_hash_table
{
struct bfd_link_hash_table root;
/* Vector of shared libraries. */
struct vector_type shrlibs;
/* Fixup section. */
asection *fixup;
/* Base address. Used by fixups. */
bfd_vma base_addr;
};
#define alpha_vms_link_hash(INFO) \
((struct alpha_vms_link_hash_table *)(INFO->hash))
/* Alpha VMS linker hash table entry. */
struct alpha_vms_link_hash_entry
{
struct bfd_link_hash_entry root;
/* Pointer to the original vms symbol. */
struct vms_symbol_entry *sym;
};
/* Image reading. */
/* Read & process EIHD record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_eihd (bfd *abfd, unsigned int *eisd_offset,
unsigned int *eihs_offset)
{
unsigned int imgtype, size;
bfd_vma symvva;
struct vms_eihd *eihd = (struct vms_eihd *)PRIV (recrd.rec);
vms_debug2 ((8, "_bfd_vms_slurp_eihd\n"));
size = bfd_getl32 (eihd->size);
imgtype = bfd_getl32 (eihd->imgtype);
if (imgtype == EIHD__K_EXE || imgtype == EIHD__K_LIM)
abfd->flags |= EXEC_P;
symvva = bfd_getl64 (eihd->symvva);
if (symvva != 0)
{
PRIV (symvva) = symvva;
abfd->flags |= DYNAMIC;
}
PRIV (ident) = bfd_getl32 (eihd->ident);
PRIV (matchctl) = eihd->matchctl;
*eisd_offset = bfd_getl32 (eihd->isdoff);
*eihs_offset = bfd_getl32 (eihd->symdbgoff);
vms_debug2 ((4, "EIHD size %d imgtype %d symvva 0x%lx eisd %d eihs %d\n",
size, imgtype, (unsigned long)symvva,
*eisd_offset, *eihs_offset));
return TRUE;
}
/* Read & process EISD record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_eisd (bfd *abfd, unsigned int offset)
{
int section_count = 0;
vms_debug2 ((8, "_bfd_vms_slurp_eisd\n"));
while (1)
{
struct vms_eisd *eisd;
unsigned int rec_size;
unsigned int size;
unsigned long long vaddr;
unsigned int flags;
unsigned int vbn;
char *name = NULL;
asection *section;
flagword bfd_flags;
/* PR 17512: file: 3d9e9fe9. */
if (offset >= PRIV (recrd.rec_size))
return FALSE;
eisd = (struct vms_eisd *)(PRIV (recrd.rec) + offset);
rec_size = bfd_getl32 (eisd->eisdsize);
if (rec_size == 0)
break;
/* Skip to next block if pad. */
if (rec_size == 0xffffffff)
{
offset = (offset + VMS_BLOCK_SIZE) & ~(VMS_BLOCK_SIZE - 1);
continue;
}
else
offset += rec_size;
size = bfd_getl32 (eisd->secsize);
vaddr = bfd_getl64 (eisd->virt_addr);
flags = bfd_getl32 (eisd->flags);
vbn = bfd_getl32 (eisd->vbn);
vms_debug2 ((4, "EISD at 0x%x size 0x%x addr 0x%lx flags 0x%x blk %d\n",
offset, size, (unsigned long)vaddr, flags, vbn));
/* 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 (vbn != 0)
bfd_flags |= SEC_HAS_CONTENTS | SEC_LOAD;
if (flags & EISD__M_EXE)
bfd_flags |= SEC_CODE;
if (flags & EISD__M_NONSHRADR)
bfd_flags |= SEC_DATA;
if (!(flags & EISD__M_WRT))
bfd_flags |= SEC_READONLY;
if (flags & EISD__M_DZRO)
bfd_flags |= SEC_DATA;
if (flags & EISD__M_FIXUPVEC)
bfd_flags |= SEC_DATA;
if (flags & EISD__M_CRF)
bfd_flags |= SEC_DATA;
if (flags & EISD__M_GBL)
{
name = _bfd_vms_save_counted_string (eisd->gblnam);
bfd_flags |= SEC_COFF_SHARED_LIBRARY;
bfd_flags &= ~(SEC_ALLOC | SEC_LOAD);
}
else if (flags & EISD__M_FIXUPVEC)
name = "$FIXUPVEC$";
else if (eisd->type == EISD__K_USRSTACK)
name = "$STACK$";
else
{
const char *pfx;
name = (char*) bfd_alloc (abfd, 32);
if (flags & EISD__M_DZRO)
pfx = "BSS";
else if (flags & EISD__M_EXE)
pfx = "CODE";
else if (!(flags & EISD__M_WRT))
pfx = "RO";
else
pfx = "LOCAL";
BFD_ASSERT (section_count < 999);
sprintf (name, "$%s_%03d$", pfx, section_count++);
}
section = bfd_make_section (abfd, name);
if (!section)
return FALSE;
section->filepos = vbn ? VMS_BLOCK_SIZE * (vbn - 1) : 0;
section->size = size;
section->vma = vaddr;
if (!bfd_set_section_flags (abfd, section, bfd_flags))
return FALSE;
}
return TRUE;
}
/* Read & process EIHS record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_eihs (bfd *abfd, unsigned int offset)
{
unsigned char *p = PRIV (recrd.rec) + offset;
unsigned int gstvbn = bfd_getl32 (p + EIHS__L_GSTVBN);
unsigned int gstsize ATTRIBUTE_UNUSED = bfd_getl32 (p + EIHS__L_GSTSIZE);
unsigned int dstvbn = bfd_getl32 (p + EIHS__L_DSTVBN);
unsigned int dstsize = bfd_getl32 (p + EIHS__L_DSTSIZE);
unsigned int dmtvbn = bfd_getl32 (p + EIHS__L_DMTVBN);
unsigned int dmtbytes = bfd_getl32 (p + EIHS__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 FALSE;
section->size = dstsize;
section->filepos = VMS_BLOCK_SIZE * (dstvbn - 1);
if (!bfd_set_section_flags (abfd, section, bfd_flags))
return FALSE;
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 FALSE;
section->size = dmtbytes;
section->filepos = VMS_BLOCK_SIZE * (dmtvbn - 1);
if (!bfd_set_section_flags (abfd, section, bfd_flags))
return FALSE;
}
if (gstvbn)
{
if (bfd_seek (abfd, VMS_BLOCK_SIZE * (gstvbn - 1), SEEK_SET))
{
bfd_set_error (bfd_error_file_truncated);
return FALSE;
}
if (_bfd_vms_slurp_object_records (abfd) != TRUE)
return FALSE;
abfd->flags |= HAS_SYMS;
}
return TRUE;
}
/* Object file reading. */
/* Object file input functions. */
/* Get next record from object file to vms_buf.
Set PRIV(buf_size) and return it
This is a little tricky since it should be portable.
The openVMS object file has 'variable length' which means that
read() returns data in chunks of (hopefully) correct and expected
size. The linker (and other tools on VMS) depend on that. Unix
doesn't know about 'formatted' files, so reading and writing such
an object file in a Unix environment is not trivial.
With the tool 'file' (available on all VMS FTP sites), one
can view and change the attributes of a file. Changing from
'variable length' to 'fixed length, 512 bytes' reveals the
record size at the first 2 bytes of every record. The same
may happen during the transfer of object files from VMS to Unix,
at least with UCX, the DEC implementation of TCP/IP.
The VMS format repeats the size at bytes 2 & 3 of every record.
On the first call (file_format == FF_UNKNOWN) we check if
the first and the third byte pair (!) of the record match.
If they do it's an object file in an Unix environment or with
wrong attributes (FF_FOREIGN), else we should be in a VMS
environment where read() returns the record size (FF_NATIVE).
Reading is always done in 2 steps:
1. first just the record header is read and the size extracted,
2. then the read buffer is adjusted and the remaining bytes are
read in.
All file I/O is done on even file positions. */
#define VMS_OBJECT_ADJUSTMENT 2
static void
maybe_adjust_record_pointer_for_object (bfd *abfd)
{
/* Set the file format once for all on the first invocation. */
if (PRIV (recrd.file_format) == FF_UNKNOWN)
{
if (PRIV (recrd.rec)[0] == PRIV (recrd.rec)[4]
&& PRIV (recrd.rec)[1] == PRIV (recrd.rec)[5])
PRIV (recrd.file_format) = FF_FOREIGN;
else
PRIV (recrd.file_format) = FF_NATIVE;
}
/* The adjustment is needed only in an Unix environment. */
if (PRIV (recrd.file_format) == FF_FOREIGN)
PRIV (recrd.rec) += VMS_OBJECT_ADJUSTMENT;
}
/* Implement step #1 of the object record reading procedure.
Return the record type or -1 on failure. */
static int
_bfd_vms_get_object_record (bfd *abfd)
{
unsigned int test_len = 6;
int type;
vms_debug2 ((8, "_bfd_vms_get_obj_record\n"));
/* Skip alignment byte if the current position is odd. */
if (PRIV (recrd.file_format) == FF_FOREIGN && (bfd_tell (abfd) & 1))
{
if (bfd_bread (PRIV (recrd.buf), 1, abfd) != 1)
{
bfd_set_error (bfd_error_file_truncated);
return -1;
}
}
/* Read the record header */
if (bfd_bread (PRIV (recrd.buf), test_len, abfd) != test_len)
{
bfd_set_error (bfd_error_file_truncated);
return -1;
}
/* Reset the record pointer. */
PRIV (recrd.rec) = PRIV (recrd.buf);
maybe_adjust_record_pointer_for_object (abfd);
if (vms_get_remaining_object_record (abfd, test_len) <= 0)
return -1;
type = bfd_getl16 (PRIV (recrd.rec));
vms_debug2 ((8, "_bfd_vms_get_obj_record: rec %p, size %d, type %d\n",
PRIV (recrd.rec), PRIV (recrd.rec_size), type));
return type;
}
/* Implement step #2 of the object record reading procedure.
Return the size of the record or 0 on failure. */
static int
vms_get_remaining_object_record (bfd *abfd, unsigned int read_so_far)
{
unsigned int to_read;
vms_debug2 ((8, "vms_get_remaining_obj_record\n"));
/* Extract record size. */
PRIV (recrd.rec_size) = bfd_getl16 (PRIV (recrd.rec) + 2);
if (PRIV (recrd.rec_size) == 0)
{
bfd_set_error (bfd_error_file_truncated);
return 0;
}
/* That's what the linker manual says. */
if (PRIV (recrd.rec_size) > EOBJ__C_MAXRECSIZ)
{
bfd_set_error (bfd_error_file_truncated);
return 0;
}
/* Take into account object adjustment. */
to_read = PRIV (recrd.rec_size);
if (PRIV (recrd.file_format) == FF_FOREIGN)
to_read += VMS_OBJECT_ADJUSTMENT;
/* Adjust the buffer. */
if (to_read > PRIV (recrd.buf_size))
{
PRIV (recrd.buf)
= (unsigned char *) bfd_realloc (PRIV (recrd.buf), to_read);
if (PRIV (recrd.buf) == NULL)
return 0;
PRIV (recrd.buf_size) = to_read;
}
/* PR 17512: file: 025-1974-0.004. */
else if (to_read <= read_so_far)
return 0;
/* Read the remaining record. */
to_read -= read_so_far;
vms_debug2 ((8, "vms_get_remaining_obj_record: to_read %d\n", to_read));
if (bfd_bread (PRIV (recrd.buf) + read_so_far, to_read, abfd) != to_read)
{
bfd_set_error (bfd_error_file_truncated);
return 0;
}
/* Reset the record pointer. */
PRIV (recrd.rec) = PRIV (recrd.buf);
maybe_adjust_record_pointer_for_object (abfd);
vms_debug2 ((8, "vms_get_remaining_obj_record: size %d\n",
PRIV (recrd.rec_size)));
return PRIV (recrd.rec_size);
}
/* Read and process emh record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_ehdr (bfd *abfd)
{
unsigned char *ptr;
unsigned char *vms_rec;
unsigned char *end;
int subtype;
vms_rec = PRIV (recrd.rec);
/* PR 17512: file: 62736583. */
end = PRIV (recrd.buf) + PRIV (recrd.buf_size);
vms_debug2 ((2, "HDR/EMH\n"));
subtype = bfd_getl16 (vms_rec + 4);
vms_debug2 ((3, "subtype %d\n", subtype));
switch (subtype)
{
case EMH__C_MHD:
/* Module header. */
if (vms_rec + 21 >= end)
goto fail;
PRIV (hdr_data).hdr_b_strlvl = vms_rec[6];
PRIV (hdr_data).hdr_l_arch1 = bfd_getl32 (vms_rec + 8);
PRIV (hdr_data).hdr_l_arch2 = bfd_getl32 (vms_rec + 12);
PRIV (hdr_data).hdr_l_recsiz = bfd_getl32 (vms_rec + 16);
if ((vms_rec + 20 + vms_rec[20] + 1) >= end)
goto fail;
PRIV (hdr_data).hdr_t_name = _bfd_vms_save_counted_string (vms_rec + 20);
ptr = vms_rec + 20 + vms_rec[20] + 1;
if ((ptr + *ptr + 1) >= end)
goto fail;
PRIV (hdr_data).hdr_t_version =_bfd_vms_save_counted_string (ptr);
ptr += *ptr + 1;
if (ptr + 17 >= end)
goto fail;
PRIV (hdr_data).hdr_t_date = _bfd_vms_save_sized_string (ptr, 17);
break;
case EMH__C_LNM:
if (vms_rec + PRIV (recrd.rec_size - 6) > end)
goto fail;
PRIV (hdr_data).hdr_c_lnm =
_bfd_vms_save_sized_string (vms_rec, PRIV (recrd.rec_size - 6));
break;
case EMH__C_SRC:
if (vms_rec + PRIV (recrd.rec_size - 6) > end)
goto fail;
PRIV (hdr_data).hdr_c_src =
_bfd_vms_save_sized_string (vms_rec, PRIV (recrd.rec_size - 6));
break;
case EMH__C_TTL:
if (vms_rec + PRIV (recrd.rec_size - 6) > end)
goto fail;
PRIV (hdr_data).hdr_c_ttl =
_bfd_vms_save_sized_string (vms_rec, PRIV (recrd.rec_size - 6));
break;
case EMH__C_CPR:
case EMH__C_MTC:
case EMH__C_GTX:
break;
default:
fail:
bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
return TRUE;
}
/* Typical sections for evax object files. */
#define EVAX_ABS_NAME "$ABS$"
#define EVAX_CODE_NAME "$CODE$"
#define EVAX_LINK_NAME "$LINK$"
#define EVAX_DATA_NAME "$DATA$"
#define EVAX_BSS_NAME "$BSS$"
#define EVAX_READONLYADDR_NAME "$READONLY_ADDR$"
#define EVAX_READONLY_NAME "$READONLY$"
#define EVAX_LITERAL_NAME "$LITERAL$"
#define EVAX_LITERALS_NAME "$LITERALS"
#define EVAX_COMMON_NAME "$COMMON$"
#define EVAX_LOCAL_NAME "$LOCAL$"
struct sec_flags_struct
{
const char *name; /* Name of section. */
int vflags_always;
flagword flags_always; /* Flags we set always. */
int vflags_hassize;
flagword flags_hassize; /* Flags we set if the section has a size > 0. */
};
/* These flags are deccrtl/vaxcrtl (openVMS 6.2 Alpha) compatible. */
static const struct sec_flags_struct evax_section_flags[] =
{
{ EVAX_ABS_NAME,
EGPS__V_SHR,
0,
EGPS__V_SHR,
0 },
{ EVAX_CODE_NAME,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_SHR | EGPS__V_EXE,
SEC_CODE | SEC_READONLY,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_SHR | EGPS__V_EXE,
SEC_CODE | SEC_READONLY | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_LITERAL_NAME,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_SHR | EGPS__V_RD | EGPS__V_NOMOD,
SEC_DATA | SEC_READONLY,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_SHR | EGPS__V_RD,
SEC_DATA | SEC_READONLY | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_LINK_NAME,
EGPS__V_REL | EGPS__V_RD,
SEC_DATA | SEC_READONLY,
EGPS__V_REL | EGPS__V_RD,
SEC_DATA | SEC_READONLY | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_DATA_NAME,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT | EGPS__V_NOMOD,
SEC_DATA,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT,
SEC_DATA | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_BSS_NAME,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT | EGPS__V_NOMOD,
SEC_NO_FLAGS,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT | EGPS__V_NOMOD,
SEC_ALLOC },
{ EVAX_READONLYADDR_NAME,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_RD,
SEC_DATA | SEC_READONLY,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_RD,
SEC_DATA | SEC_READONLY | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_READONLY_NAME,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_SHR | EGPS__V_RD | EGPS__V_NOMOD,
SEC_DATA | SEC_READONLY,
EGPS__V_PIC | EGPS__V_REL | EGPS__V_SHR | EGPS__V_RD,
SEC_DATA | SEC_READONLY | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_LOCAL_NAME,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT,
SEC_DATA,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT,
SEC_DATA | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ EVAX_LITERALS_NAME,
EGPS__V_PIC | EGPS__V_OVR,
SEC_DATA | SEC_READONLY,
EGPS__V_PIC | EGPS__V_OVR,
SEC_DATA | SEC_READONLY | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD },
{ NULL,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT,
SEC_DATA,
EGPS__V_REL | EGPS__V_RD | EGPS__V_WRT,
SEC_DATA | SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD }
};
/* Retrieve BFD section flags by name and size. */
static flagword
vms_secflag_by_name (const struct sec_flags_struct *section_flags,
const char *name,
int hassize)
{
int i = 0;
while (section_flags[i].name != NULL)
{
if (strcmp (name, section_flags[i].name) == 0)
{
if (hassize)
return section_flags[i].flags_hassize;
else
return section_flags[i].flags_always;
}
i++;
}
if (hassize)
return section_flags[i].flags_hassize;
return section_flags[i].flags_always;
}
/* Retrieve VMS section flags by name and size. */
static flagword
vms_esecflag_by_name (const struct sec_flags_struct *section_flags,
const char *name,
int hassize)
{
int i = 0;
while (section_flags[i].name != NULL)
{
if (strcmp (name, section_flags[i].name) == 0)
{
if (hassize)
return section_flags[i].vflags_hassize;
else
return section_flags[i].vflags_always;
}
i++;
}
if (hassize)
return section_flags[i].vflags_hassize;
return section_flags[i].vflags_always;
}
/* Add SYM to the symbol table of ABFD.
Return FALSE in case of error. */
static bfd_boolean
add_symbol_entry (bfd *abfd, struct vms_symbol_entry *sym)
{
if (PRIV (gsd_sym_count) >= PRIV (max_sym_count))
{
if (PRIV (max_sym_count) == 0)
{
PRIV (max_sym_count) = 128;
PRIV (syms) = bfd_malloc
(PRIV (max_sym_count) * sizeof (struct vms_symbol_entry *));
}
else
{
PRIV (max_sym_count) *= 2;
PRIV (syms) = bfd_realloc
(PRIV (syms),
(PRIV (max_sym_count) * sizeof (struct vms_symbol_entry *)));
}
if (PRIV (syms) == NULL)
return FALSE;
}
PRIV (syms)[PRIV (gsd_sym_count)++] = sym;
return TRUE;
}
/* Create a symbol whose name is ASCIC and add it to ABFD.
Return NULL in case of error. */
static struct vms_symbol_entry *
add_symbol (bfd *abfd, const unsigned char *ascic)
{
struct vms_symbol_entry *entry;
int len;
len = *ascic++;
entry = (struct vms_symbol_entry *)bfd_zalloc (abfd, sizeof (*entry) + len);
if (entry == NULL)
return NULL;
entry->namelen = len;
memcpy (entry->name, ascic, len);
entry->name[len] = 0;
entry->owner = abfd;
if (!add_symbol_entry (abfd, entry))
return NULL;
return entry;
}
/* Read and process EGSD. Return FALSE on failure. */
static bfd_boolean
_bfd_vms_slurp_egsd (bfd *abfd)
{
int gsd_type, gsd_size;
unsigned char *vms_rec;
unsigned long base_addr;
vms_debug2 ((2, "EGSD\n"));
PRIV (recrd.rec) += 8; /* Skip type, size, align pad. */
PRIV (recrd.rec_size) -= 8;
/* Calculate base address for each section. */
base_addr = 0L;
while (PRIV (recrd.rec_size) > 0)
{
vms_rec = PRIV (recrd.rec);
gsd_type = bfd_getl16 (vms_rec);
gsd_size = bfd_getl16 (vms_rec + 2);
vms_debug2 ((3, "egsd_type %d\n", gsd_type));
switch (gsd_type)
{
case EGSD__C_PSC:
/* Program section definition. */
{
struct vms_egps *egps = (struct vms_egps *)vms_rec;
flagword new_flags, vms_flags;
asection *section;
vms_flags = bfd_getl16 (egps->flags);
if ((vms_flags & EGPS__V_REL) == 0)
{
/* Use the global absolute section for all
absolute sections. */
section = bfd_abs_section_ptr;
}
else
{
char *name;
unsigned long align_addr;
name = _bfd_vms_save_counted_string (&egps->namlng);
section = bfd_make_section (abfd, name);
if (!section)
return FALSE;
section->filepos = 0;
section->size = bfd_getl32 (egps->alloc);
section->alignment_power = egps->align;
vms_section_data (section)->flags = vms_flags;
vms_section_data (section)->no_flags = 0;
new_flags = vms_secflag_by_name (evax_section_flags, name,
section->size > 0);
if (section->size > 0)
new_flags |= SEC_LOAD;
if (!(vms_flags & EGPS__V_NOMOD) && section->size > 0)
{
/* Set RELOC and HAS_CONTENTS if the section is not
demand-zero and not empty. */
new_flags |= SEC_HAS_CONTENTS;
if (vms_flags & EGPS__V_REL)
new_flags |= SEC_RELOC;
}
if (vms_flags & EGPS__V_EXE)
{
/* Set CODE if section is executable. */
new_flags |= SEC_CODE;
new_flags &= ~SEC_DATA;
}
if (!bfd_set_section_flags (abfd, section, new_flags))
return FALSE;
/* Give a non-overlapping vma to non absolute sections. */
align_addr = (1 << section->alignment_power);
if ((base_addr % align_addr) != 0)
base_addr += (align_addr - (base_addr % align_addr));
section->vma = (bfd_vma)base_addr;
base_addr += section->size;
}
/* Append it to the section array. */
if (PRIV (section_count) >= PRIV (section_max))
{
if (PRIV (section_max) == 0)
PRIV (section_max) = 16;
else
PRIV (section_max) *= 2;
PRIV (sections) = bfd_realloc_or_free
(PRIV (sections), PRIV (section_max) * sizeof (asection *));
if (PRIV (sections) == NULL)
return FALSE;
}
PRIV (sections)[PRIV (section_count)] = section;
PRIV (section_count)++;
}
break;
case EGSD__C_SYM:
{
int nameoff;
struct vms_symbol_entry *entry;
struct vms_egsy *egsy = (struct vms_egsy *) vms_rec;
flagword old_flags;
old_flags = bfd_getl16 (egsy->flags);
if (old_flags & EGSY__V_DEF)
nameoff = ESDF__B_NAMLNG;
else
nameoff = ESRF__B_NAMLNG;
entry = add_symbol (abfd, vms_rec + nameoff);
if (entry == NULL)
return FALSE;
/* Allow only duplicate reference. */
if ((entry->flags & EGSY__V_DEF) && (old_flags & EGSY__V_DEF))
abort ();
if (entry->typ == 0)
{
entry->typ = gsd_type;
entry->data_type = egsy->datyp;
entry->flags = old_flags;
}
if (old_flags & EGSY__V_DEF)
{
struct vms_esdf *esdf = (struct vms_esdf *)vms_rec;
entry->value = bfd_getl64 (esdf->value);
entry->section = PRIV (sections)[bfd_getl32 (esdf->psindx)];
if (old_flags & EGSY__V_NORM)
{
PRIV (norm_sym_count)++;
entry->code_value = bfd_getl64 (esdf->code_address);
entry->code_section =
PRIV (sections)[bfd_getl32 (esdf->ca_psindx)];
}
}
}
break;
case EGSD__C_SYMG:
{
struct vms_symbol_entry *entry;
struct vms_egst *egst = (struct vms_egst *)vms_rec;
flagword old_flags;
old_flags = bfd_getl16 (egst->header.flags);
entry = add_symbol (abfd, &egst->namlng);
if (entry == NULL)
return FALSE;
entry->typ = gsd_type;
entry->data_type = egst->header.datyp;
entry->flags = old_flags;
entry->symbol_vector = bfd_getl32 (egst->value);
if (old_flags & EGSY__V_REL)
entry->section = PRIV (sections)[bfd_getl32 (egst->psindx)];
else
entry->section = bfd_abs_section_ptr;
entry->value = bfd_getl64 (egst->lp_2);
if (old_flags & EGSY__V_NORM)
{
PRIV (norm_sym_count)++;
entry->code_value = bfd_getl64 (egst->lp_1);
entry->code_section = bfd_abs_section_ptr;
}
}
break;
case EGSD__C_SPSC:
case EGSD__C_IDC:
/* Currently ignored. */
break;
case EGSD__C_SYMM:
case EGSD__C_SYMV:
default:
(*_bfd_error_handler) (_("Unknown EGSD subtype %d"), gsd_type);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
PRIV (recrd.rec_size) -= gsd_size;
PRIV (recrd.rec) += gsd_size;
}
if (PRIV (gsd_sym_count) > 0)
abfd->flags |= HAS_SYMS;
return TRUE;
}
/* Stack routines for vms ETIR commands. */
/* Push value and section index. */
static void
_bfd_vms_push (bfd *abfd, bfd_vma val, unsigned int reloc)
{
vms_debug2 ((4, "<push %08lx (0x%08x) at %d>\n",
(unsigned long)val, reloc, PRIV (stackptr)));
PRIV (stack[PRIV (stackptr)]).value = val;
PRIV (stack[PRIV (stackptr)]).reloc = reloc;
PRIV (stackptr)++;
if (PRIV (stackptr) >= STACKSIZE)
{
bfd_set_error (bfd_error_bad_value);
(*_bfd_error_handler) (_("Stack overflow (%d) in _bfd_vms_push"), PRIV (stackptr));
exit (1);
}
}
/* Pop value and section index. */
static void
_bfd_vms_pop (bfd *abfd, bfd_vma *val, unsigned int *rel)
{
if (PRIV (stackptr) == 0)
{
bfd_set_error (bfd_error_bad_value);
(*_bfd_error_handler) (_("Stack underflow in _bfd_vms_pop"));
exit (1);
}
PRIV (stackptr)--;
*val = PRIV (stack[PRIV (stackptr)]).value;
*rel = PRIV (stack[PRIV (stackptr)]).reloc;
vms_debug2 ((4, "<pop %08lx (0x%08x)>\n", (unsigned long)*val, *rel));
}
/* Routines to fill sections contents during tir/etir read. */
/* Initialize image buffer pointer to be filled. */
static void
image_set_ptr (bfd *abfd, bfd_vma vma, int sect, struct bfd_link_info *info)
{
asection *sec;
vms_debug2 ((4, "image_set_ptr (0x%08x, sect=%d)\n", (unsigned)vma, sect));
sec = PRIV (sections)[sect];
if (info)
{
/* Reading contents to an output bfd. */
if (sec->output_section == NULL)
{
/* Section discarded. */
vms_debug2 ((5, " section %s discarded\n", sec->name));
/* This is not used. */
PRIV (image_section) = NULL;
PRIV (image_offset) = 0;
return;
}
PRIV (image_offset) = sec->output_offset + vma;
PRIV (image_section) = sec->output_section;
}
else
{
PRIV (image_offset) = vma;
PRIV (image_section) = sec;
}
}
/* Increment image buffer pointer by offset. */
static void
image_inc_ptr (bfd *abfd, bfd_vma offset)
{
vms_debug2 ((4, "image_inc_ptr (%u)\n", (unsigned)offset));
PRIV (image_offset) += offset;
}
/* Save current DST location counter under specified index. */
static void
dst_define_location (bfd *abfd, unsigned int loc)
{
vms_debug2 ((4, "dst_define_location (%d)\n", (int)loc));
/* Grow the ptr offset table if necessary. */
if (loc + 1 > PRIV (dst_ptr_offsets_count))
{
PRIV (dst_ptr_offsets) = bfd_realloc (PRIV (dst_ptr_offsets),
(loc + 1) * sizeof (unsigned int));
PRIV (dst_ptr_offsets_count) = loc + 1;
}
PRIV (dst_ptr_offsets)[loc] = PRIV (image_offset);
}
/* Restore saved DST location counter from specified index. */
static void
dst_restore_location (bfd *abfd, unsigned int loc)
{
vms_debug2 ((4, "dst_restore_location (%d)\n", (int)loc));
PRIV (image_offset) = PRIV (dst_ptr_offsets)[loc];
}
/* Retrieve saved DST location counter from specified index. */
static unsigned int
dst_retrieve_location (bfd *abfd, unsigned int loc)
{
vms_debug2 ((4, "dst_retrieve_location (%d)\n", (int)loc));
return PRIV (dst_ptr_offsets)[loc];
}
/* Write multiple bytes to section image. */
static bfd_boolean
image_write (bfd *abfd, unsigned char *ptr, int size)
{
#if VMS_DEBUG
_bfd_vms_debug (8, "image_write from (%p, %d) to (%ld)\n", ptr, size,
(long)PRIV (image_offset));
_bfd_hexdump (9, ptr, size, 0);
#endif
if (PRIV (image_section)->contents != NULL)
{
asection *sec = PRIV (image_section);
file_ptr off = PRIV (image_offset);
/* Check bounds. */
if (off > (file_ptr)sec->size
|| size > (file_ptr)sec->size
|| off + size > (file_ptr)sec->size)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
memcpy (sec->contents + off, ptr, size);
}
PRIV (image_offset) += size;
return TRUE;
}
/* Write byte to section image. */
static bfd_boolean
image_write_b (bfd * abfd, unsigned int value)
{
unsigned char data[1];
vms_debug2 ((6, "image_write_b (%02x)\n", (int) value));
*data = value;
return image_write (abfd, data, sizeof (data));
}
/* Write 2-byte word to image. */
static bfd_boolean
image_write_w (bfd * abfd, unsigned int value)
{
unsigned char data[2];
vms_debug2 ((6, "image_write_w (%04x)\n", (int) value));
bfd_putl16 (value, data);
return image_write (abfd, data, sizeof (data));
}
/* Write 4-byte long to image. */
static bfd_boolean
image_write_l (bfd * abfd, unsigned long value)
{
unsigned char data[4];
vms_debug2 ((6, "image_write_l (%08lx)\n", value));
bfd_putl32 (value, data);
return image_write (abfd, data, sizeof (data));
}
/* Write 8-byte quad to image. */
static bfd_boolean
image_write_q (bfd * abfd, bfd_vma value)
{
unsigned char data[8];
vms_debug2 ((6, "image_write_q (%08lx)\n", (unsigned long)value));
bfd_putl64 (value, data);
return image_write (abfd, data, sizeof (data));
}
static const char *
_bfd_vms_etir_name (int cmd)
{
switch (cmd)
{
case ETIR__C_STA_GBL: return "ETIR__C_STA_GBL";
case ETIR__C_STA_LW: return "ETIR__C_STA_LW";
case ETIR__C_STA_QW: return "ETIR__C_STA_QW";
case ETIR__C_STA_PQ: return "ETIR__C_STA_PQ";
case ETIR__C_STA_LI: return "ETIR__C_STA_LI";
case ETIR__C_STA_MOD: return "ETIR__C_STA_MOD";
case ETIR__C_STA_CKARG: return "ETIR__C_STA_CKARG";
case ETIR__C_STO_B: return "ETIR__C_STO_B";
case ETIR__C_STO_W: return "ETIR__C_STO_W";
case ETIR__C_STO_GBL: return "ETIR__C_STO_GBL";
case ETIR__C_STO_CA: return "ETIR__C_STO_CA";
case ETIR__C_STO_RB: return "ETIR__C_STO_RB";
case ETIR__C_STO_AB: return "ETIR__C_STO_AB";
case ETIR__C_STO_OFF: return "ETIR__C_STO_OFF";
case ETIR__C_STO_IMM: return "ETIR__C_STO_IMM";
case ETIR__C_STO_IMMR: return "ETIR__C_STO_IMMR";
case ETIR__C_STO_LW: return "ETIR__C_STO_LW";
case ETIR__C_STO_QW: return "ETIR__C_STO_QW";
case ETIR__C_STO_GBL_LW: return "ETIR__C_STO_GBL_LW";
case ETIR__C_STO_LP_PSB: return "ETIR__C_STO_LP_PSB";
case ETIR__C_STO_HINT_GBL: return "ETIR__C_STO_HINT_GBL";
case ETIR__C_STO_HINT_PS: return "ETIR__C_STO_HINT_PS";
case ETIR__C_OPR_ADD: return "ETIR__C_OPR_ADD";
case ETIR__C_OPR_SUB: return "ETIR__C_OPR_SUB";
case ETIR__C_OPR_INSV: return "ETIR__C_OPR_INSV";
case ETIR__C_OPR_USH: return "ETIR__C_OPR_USH";
case ETIR__C_OPR_ROT: return "ETIR__C_OPR_ROT";
case ETIR__C_OPR_REDEF: return "ETIR__C_OPR_REDEF";
case ETIR__C_OPR_DFLIT: return "ETIR__C_OPR_DFLIT";
case ETIR__C_STC_LP: return "ETIR__C_STC_LP";
case ETIR__C_STC_GBL: return "ETIR__C_STC_GBL";
case ETIR__C_STC_GCA: return "ETIR__C_STC_GCA";
case ETIR__C_STC_PS: return "ETIR__C_STC_PS";
case ETIR__C_STC_NBH_PS: return "ETIR__C_STC_NBH_PS";
case ETIR__C_STC_NOP_GBL: return "ETIR__C_STC_NOP_GBL";
case ETIR__C_STC_NOP_PS: return "ETIR__C_STC_NOP_PS";
case ETIR__C_STC_BSR_GBL: return "ETIR__C_STC_BSR_GBL";
case ETIR__C_STC_BSR_PS: return "ETIR__C_STC_BSR_PS";
case ETIR__C_STC_LDA_GBL: return "ETIR__C_STC_LDA_GBL";
case ETIR__C_STC_LDA_PS: return "ETIR__C_STC_LDA_PS";
case ETIR__C_STC_BOH_GBL: return "ETIR__C_STC_BOH_GBL";
case ETIR__C_STC_BOH_PS: return "ETIR__C_STC_BOH_PS";
case ETIR__C_STC_NBH_GBL: return "ETIR__C_STC_NBH_GBL";
case ETIR__C_STC_LP_PSB: return "ETIR__C_STC_LP_PSB";
case ETIR__C_CTL_SETRB: return "ETIR__C_CTL_SETRB";
case ETIR__C_CTL_AUGRB: return "ETIR__C_CTL_AUGRB";
case ETIR__C_CTL_DFLOC: return "ETIR__C_CTL_DFLOC";
case ETIR__C_CTL_STLOC: return "ETIR__C_CTL_STLOC";
case ETIR__C_CTL_STKDL: return "ETIR__C_CTL_STKDL";
default:
/* These names have not yet been added to this switch statement. */
(*_bfd_error_handler) (_("unknown ETIR command %d"), cmd);
}
return NULL;
}
#define HIGHBIT(op) ((op & 0x80000000L) == 0x80000000L)
static void
_bfd_vms_get_value (bfd *abfd, const unsigned char *ascic,
struct bfd_link_info *info,
bfd_vma *vma,
struct alpha_vms_link_hash_entry **hp)
{
char name[257];
int len;
int i;
struct alpha_vms_link_hash_entry *h;
/* Not linking. Do not try to resolve the symbol. */
if (info == NULL)
{
*vma = 0;
*hp = NULL;
return;
}
len = *ascic;
for (i = 0; i < len; i++)
name[i] = ascic[i + 1];
name[i] = 0;
h = (struct alpha_vms_link_hash_entry *)
bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, TRUE);
*hp = h;
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
*vma = h->root.u.def.value
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma;
else if (h && h->root.type == bfd_link_hash_undefweak)
*vma = 0;
else
{
(*info->callbacks->undefined_symbol)
(info, name, abfd, PRIV (image_section), PRIV (image_offset), TRUE);
*vma = 0;
}
}
#define RELC_NONE 0
#define RELC_REL 1
#define RELC_SHR_BASE 0x10000
#define RELC_SEC_BASE 0x20000
#define RELC_MASK 0x0ffff
static unsigned int
alpha_vms_sym_to_ctxt (struct alpha_vms_link_hash_entry *h)
{
/* Handle undefined symbols. */
if (h == NULL || h->sym == NULL)
return RELC_NONE;
if (h->sym->typ == EGSD__C_SYMG)
{
if (h->sym->flags & EGSY__V_REL)
return RELC_SHR_BASE + PRIV2 (h->sym->owner, shr_index);
else
{
/* Can this happen (non-relocatable symg) ? I'd like to see
an example. */
abort ();
}
}
if (h->sym->typ == EGSD__C_SYM)
{
if (h->sym->flags & EGSY__V_REL)
return RELC_REL;
else
return RELC_NONE;
}
abort ();
}
static bfd_vma
alpha_vms_get_sym_value (asection *sect, bfd_vma addr)
{
return sect->output_section->vma + sect->output_offset + addr;
}
static bfd_vma
alpha_vms_fix_sec_rel (bfd *abfd, struct bfd_link_info *info,
unsigned int rel, bfd_vma vma)
{
asection *sec = PRIV (sections)[rel & RELC_MASK];
if (info)
{
if (sec->output_section == NULL)
abort ();
return vma + sec->output_section->vma + sec->output_offset;
}
else
return vma + sec->vma;
}
/* Read an ETIR record from ABFD. If INFO is not null, put the content into
the output section (used during linking).
Return FALSE in case of error. */
static bfd_boolean
_bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
{
unsigned char *ptr;
unsigned int length;
unsigned char *maxptr;
bfd_vma op1;
bfd_vma op2;
unsigned int rel1;
unsigned int rel2;
struct alpha_vms_link_hash_entry *h;
PRIV (recrd.rec) += ETIR__C_HEADER_SIZE;
PRIV (recrd.rec_size) -= ETIR__C_HEADER_SIZE;
ptr = PRIV (recrd.rec);
length = PRIV (recrd.rec_size);
maxptr = ptr + length;
vms_debug2 ((2, "ETIR: %d bytes\n", length));
while (ptr < maxptr)
{
int cmd = bfd_getl16 (ptr);
int cmd_length = bfd_getl16 (ptr + 2);
ptr += 4;
#if VMS_DEBUG
_bfd_vms_debug (4, "etir: %s(%d)\n",
_bfd_vms_etir_name (cmd), cmd);
_bfd_hexdump (8, ptr, cmd_length - 4, 0);
#endif
switch (cmd)
{
/* Stack global
arg: cs symbol name
stack 32 bit value of symbol (high bits set to 0). */
case ETIR__C_STA_GBL:
_bfd_vms_get_value (abfd, ptr, info, &op1, &h);
_bfd_vms_push (abfd, op1, alpha_vms_sym_to_ctxt (h));
break;
/* Stack longword
arg: lw value
stack 32 bit value, sign extend to 64 bit. */
case ETIR__C_STA_LW:
_bfd_vms_push (abfd, bfd_getl32 (ptr), RELC_NONE);
break;
/* Stack quadword
arg: qw value
stack 64 bit value of symbol. */
case ETIR__C_STA_QW:
_bfd_vms_push (abfd, bfd_getl64 (ptr), RELC_NONE);
break;
/* Stack psect base plus quadword offset
arg: lw section index
qw signed quadword offset (low 32 bits)
Stack qw argument and section index
(see ETIR__C_STO_OFF, ETIR__C_CTL_SETRB). */
case ETIR__C_STA_PQ:
{
int psect;
psect = bfd_getl32 (ptr);
if ((unsigned int) psect >= PRIV (section_count))
{
(*_bfd_error_handler) (_("bad section index in %s"),
_bfd_vms_etir_name (cmd));
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
op1 = bfd_getl64 (ptr + 4);
_bfd_vms_push (abfd, op1, psect | RELC_SEC_BASE);
}
break;
case ETIR__C_STA_LI:
case ETIR__C_STA_MOD:
case ETIR__C_STA_CKARG:
(*_bfd_error_handler) (_("unsupported STA cmd %s"),
_bfd_vms_etir_name (cmd));
return FALSE;
break;
/* Store byte: pop stack, write byte
arg: -. */
case ETIR__C_STO_B:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
image_write_b (abfd, (unsigned int) op1 & 0xff);
break;
/* Store word: pop stack, write word
arg: -. */
case ETIR__C_STO_W:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
image_write_w (abfd, (unsigned int) op1 & 0xffff);
break;
/* Store longword: pop stack, write longword
arg: -. */
case ETIR__C_STO_LW:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 & RELC_SEC_BASE)
{
op1 = alpha_vms_fix_sec_rel (abfd, info, rel1, op1);
rel1 = RELC_REL;
}
else if (rel1 & RELC_SHR_BASE)
{
alpha_vms_add_fixup_lr (info, rel1 & RELC_MASK, op1);
rel1 = RELC_NONE;
}
if (rel1 != RELC_NONE)
{
if (rel1 != RELC_REL)
abort ();
alpha_vms_add_lw_reloc (info);
}
image_write_l (abfd, op1);
break;
/* Store quadword: pop stack, write quadword
arg: -. */
case ETIR__C_STO_QW:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 & RELC_SEC_BASE)
{
op1 = alpha_vms_fix_sec_rel (abfd, info, rel1, op1);
rel1 = RELC_REL;
}
else if (rel1 & RELC_SHR_BASE)
abort ();
if (rel1 != RELC_NONE)
{
if (rel1 != RELC_REL)
abort ();
alpha_vms_add_qw_reloc (info);
}
image_write_q (abfd, op1);
break;
/* Store immediate repeated: pop stack for repeat count
arg: lw byte count
da data. */
case ETIR__C_STO_IMMR:
{
int size;
size = bfd_getl32 (ptr);
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
while (op1-- > 0)
image_write (abfd, ptr + 4, size);
}
break;
/* Store global: write symbol value
arg: cs global symbol name. */
case ETIR__C_STO_GBL:
_bfd_vms_get_value (abfd, ptr, info, &op1, &h);
if (h && h->sym)
{
if (h->sym->typ == EGSD__C_SYMG)
{
alpha_vms_add_fixup_qr
(info, abfd, h->sym->owner, h->sym->symbol_vector);
op1 = 0;
}
else
{
op1 = alpha_vms_get_sym_value (h->sym->section,
h->sym->value);
alpha_vms_add_qw_reloc (info);
}
}
image_write_q (abfd, op1);
break;
/* Store code address: write address of entry point
arg: cs global symbol name (procedure). */
case ETIR__C_STO_CA:
_bfd_vms_get_value (abfd, ptr, info, &op1, &h);
if (h && h->sym)
{
if (h->sym->flags & EGSY__V_NORM)
{
/* That's really a procedure. */
if (h->sym->typ == EGSD__C_SYMG)
{
alpha_vms_add_fixup_ca (info, abfd, h->sym->owner);
op1 = h->sym->symbol_vector;
}
else
{
op1 = alpha_vms_get_sym_value (h->sym->code_section,
h->sym->code_value);
alpha_vms_add_qw_reloc (info);
}
}
else
{
/* Symbol is not a procedure. */
abort ();
}
}
image_write_q (abfd, op1);
break;
/* Store offset to psect: pop stack, add low 32 bits to base of psect
arg: none. */
case ETIR__C_STO_OFF:
_bfd_vms_pop (abfd, &op1, &rel1);
if (!(rel1 & RELC_SEC_BASE))
abort ();
op1 = alpha_vms_fix_sec_rel (abfd, info, rel1, op1);
rel1 = RELC_REL;
image_write_q (abfd, op1);
break;
/* Store immediate
arg: lw count of bytes
da data. */
case ETIR__C_STO_IMM:
{
int size;
size = bfd_getl32 (ptr);
image_write (abfd, ptr + 4, size);
}
break;
/* This code is 'reserved to digital' according to the openVMS
linker manual, however it is generated by the DEC C compiler
and defined in the include file.
FIXME, since the following is just a guess
store global longword: store 32bit value of symbol
arg: cs symbol name. */
case ETIR__C_STO_GBL_LW:
_bfd_vms_get_value (abfd, ptr, info, &op1, &h);
#if 0
abort ();
#endif
image_write_l (abfd, op1);
break;
case ETIR__C_STO_RB:
case ETIR__C_STO_AB:
case ETIR__C_STO_LP_PSB:
(*_bfd_error_handler) (_("%s: not supported"),
_bfd_vms_etir_name (cmd));
return FALSE;
break;
case ETIR__C_STO_HINT_GBL:
case ETIR__C_STO_HINT_PS:
(*_bfd_error_handler) (_("%s: not implemented"),
_bfd_vms_etir_name (cmd));
return FALSE;
break;
/* 200 Store-conditional Linkage Pair
arg: none. */
case ETIR__C_STC_LP:
/* 202 Store-conditional Address at global address
lw linkage index
cs global name. */
case ETIR__C_STC_GBL:
/* 203 Store-conditional Code Address at global address
lw linkage index
cs procedure name. */
case ETIR__C_STC_GCA:
/* 204 Store-conditional Address at psect + offset
lw linkage index
lw psect index
qw offset. */
case ETIR__C_STC_PS:
(*_bfd_error_handler) (_("%s: not supported"),
_bfd_vms_etir_name (cmd));
return FALSE;
break;
/* 201 Store-conditional Linkage Pair with Procedure Signature
lw linkage index
cs procedure name
by signature length
da signature. */
case ETIR__C_STC_LP_PSB:
_bfd_vms_get_value (abfd, ptr + 4, info, &op1, &h);
if (h && h->sym)
{
if (h->sym->typ == EGSD__C_SYMG)
{
alpha_vms_add_fixup_lp (info, abfd, h->sym->owner);
op1 = h->sym->symbol_vector;
op2 = 0;
}
else
{
op1 = alpha_vms_get_sym_value (h->sym->code_section,
h->sym->code_value);
op2 = alpha_vms_get_sym_value (h->sym->section,
h->sym->value);
}
}
else
{
/* Undefined symbol. */
op1 = 0;
op2 = 0;
}
image_write_q (abfd, op1);
image_write_q (abfd, op2);
break;
/* 205 Store-conditional NOP at address of global
arg: none. */
case ETIR__C_STC_NOP_GBL:
/* ALPHA_R_NOP */
/* 207 Store-conditional BSR at global address
arg: none. */
case ETIR__C_STC_BSR_GBL:
/* ALPHA_R_BSR */
/* 209 Store-conditional LDA at global address
arg: none. */
case ETIR__C_STC_LDA_GBL:
/* ALPHA_R_LDA */
/* 211 Store-conditional BSR or Hint at global address
arg: none. */
case ETIR__C_STC_BOH_GBL:
/* Currentl ignored. */
break;
/* 213 Store-conditional NOP,BSR or HINT at global address
arg: none. */
case ETIR__C_STC_NBH_GBL:
/* 206 Store-conditional NOP at pect + offset
arg: none. */
case ETIR__C_STC_NOP_PS:
/* 208 Store-conditional BSR at pect + offset
arg: none. */
case ETIR__C_STC_BSR_PS:
/* 210 Store-conditional LDA at psect + offset
arg: none. */
case ETIR__C_STC_LDA_PS:
/* 212 Store-conditional BSR or Hint at pect + offset
arg: none. */
case ETIR__C_STC_BOH_PS:
/* 214 Store-conditional NOP, BSR or HINT at psect + offset
arg: none. */
case ETIR__C_STC_NBH_PS:
(*_bfd_error_handler) ("%s: not supported",
_bfd_vms_etir_name (cmd));
return FALSE;
break;
/* Det relocation base: pop stack, set image location counter
arg: none. */
case ETIR__C_CTL_SETRB:
_bfd_vms_pop (abfd, &op1, &rel1);
if (!(rel1 & RELC_SEC_BASE))
abort ();
image_set_ptr (abfd, op1, rel1 & RELC_MASK, info);
break;
/* Augment relocation base: increment image location counter by offset
arg: lw offset value. */
case ETIR__C_CTL_AUGRB:
op1 = bfd_getl32 (ptr);
image_inc_ptr (abfd, op1);
break;
/* Define location: pop index, save location counter under index
arg: none. */
case ETIR__C_CTL_DFLOC:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
dst_define_location (abfd, op1);
break;
/* Set location: pop index, restore location counter from index
arg: none. */
case ETIR__C_CTL_STLOC:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
dst_restore_location (abfd, op1);
break;
/* Stack defined location: pop index, push location counter from index
arg: none. */
case ETIR__C_CTL_STKDL:
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, dst_retrieve_location (abfd, op1), RELC_NONE);
break;
case ETIR__C_OPR_NOP: /* No-op. */
break;
case ETIR__C_OPR_ADD: /* Add. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 == RELC_NONE && rel2 != RELC_NONE)
rel1 = rel2;
else if (rel1 != RELC_NONE && rel2 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, op1 + op2, rel1);
break;
case ETIR__C_OPR_SUB: /* Subtract. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 == RELC_NONE && rel2 != RELC_NONE)
rel1 = rel2;
else if ((rel1 & RELC_SEC_BASE) && (rel2 & RELC_SEC_BASE))
{
op1 = alpha_vms_fix_sec_rel (abfd, info, rel1, op1);
op2 = alpha_vms_fix_sec_rel (abfd, info, rel2, op2);
rel1 = RELC_NONE;
}
else if (rel1 != RELC_NONE && rel2 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, op2 - op1, rel1);
break;
case ETIR__C_OPR_MUL: /* Multiply. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, op1 * op2, RELC_NONE);
break;
case ETIR__C_OPR_DIV: /* Divide. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
if (op2 == 0)
_bfd_vms_push (abfd, 0, RELC_NONE);
else
_bfd_vms_push (abfd, op2 / op1, RELC_NONE);
break;
case ETIR__C_OPR_AND: /* Logical AND. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, op1 & op2, RELC_NONE);
break;
case ETIR__C_OPR_IOR: /* Logical inclusive OR. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, op1 | op2, RELC_NONE);
break;
case ETIR__C_OPR_EOR: /* Logical exclusive OR. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, op1 ^ op2, RELC_NONE);
break;
case ETIR__C_OPR_NEG: /* Negate. */
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, -op1, RELC_NONE);
break;
case ETIR__C_OPR_COM: /* Complement. */
_bfd_vms_pop (abfd, &op1, &rel1);
if (rel1 != RELC_NONE)
goto bad_context;
_bfd_vms_push (abfd, ~op1, RELC_NONE);
break;
case ETIR__C_OPR_ASH: /* Arithmetic shift. */
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
{
bad_context:
(*_bfd_error_handler) (_("invalid use of %s with contexts"),
_bfd_vms_etir_name (cmd));
return FALSE;
}
if ((int)op2 < 0) /* Shift right. */
op1 >>= -(int)op2;
else /* Shift left. */
op1 <<= (int)op2;
_bfd_vms_push (abfd, op1, RELC_NONE); /* FIXME: sym. */
break;
case ETIR__C_OPR_INSV: /* Insert field. */
case ETIR__C_OPR_USH: /* Unsigned shift. */
case ETIR__C_OPR_ROT: /* Rotate. */
case ETIR__C_OPR_REDEF: /* Redefine symbol to current location. */
case ETIR__C_OPR_DFLIT: /* Define a literal. */
(*_bfd_error_handler) (_("%s: not supported"),
_bfd_vms_etir_name (cmd));
return FALSE;
break;
case ETIR__C_OPR_SEL: /* Select. */
_bfd_vms_pop (abfd, &op1, &rel1);
if (op1 & 0x01L)
_bfd_vms_pop (abfd, &op1, &rel1);
else
{
_bfd_vms_pop (abfd, &op1, &rel1);
_bfd_vms_pop (abfd, &op2, &rel2);
_bfd_vms_push (abfd, op1, rel1);
}
break;
default:
(*_bfd_error_handler) (_("reserved cmd %d"), cmd);
return FALSE;
break;
}
ptr += cmd_length - 4;
}
return TRUE;
}
/* Process EDBG/ETBT record.
Return TRUE on success, FALSE on error */
static bfd_boolean
vms_slurp_debug (bfd *abfd)
{
asection *section = PRIV (dst_section);
if (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
| SEC_IN_MEMORY;
section = bfd_make_section (abfd, "$DST$");
if (!section)
return FALSE;
if (!bfd_set_section_flags (abfd, section, flags))
return FALSE;
PRIV (dst_section) = section;
}
PRIV (image_section) = section;
PRIV (image_offset) = section->size;
if (!_bfd_vms_slurp_etir (abfd, NULL))
return FALSE;
section->size = PRIV (image_offset);
return TRUE;
}
/* Process EDBG record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_edbg (bfd *abfd)
{
vms_debug2 ((2, "EDBG\n"));
abfd->flags |= HAS_DEBUG | HAS_LINENO;
return vms_slurp_debug (abfd);
}
/* Process ETBT record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_etbt (bfd *abfd)
{
vms_debug2 ((2, "ETBT\n"));
abfd->flags |= HAS_LINENO;
return vms_slurp_debug (abfd);
}
/* Process EEOM record.
Return TRUE on success, FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_eeom (bfd *abfd)
{
struct vms_eeom *eeom = (struct vms_eeom *) PRIV (recrd.rec);
vms_debug2 ((2, "EEOM\n"));
PRIV (eom_data).eom_l_total_lps = bfd_getl32 (eeom->total_lps);
PRIV (eom_data).eom_w_comcod = bfd_getl16 (eeom->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);
return FALSE;
}
PRIV (eom_data).eom_has_transfer = FALSE;
if (PRIV (recrd.rec_size) > 10)
{
PRIV (eom_data).eom_has_transfer = TRUE;
PRIV (eom_data).eom_b_tfrflg = eeom->tfrflg;
PRIV (eom_data).eom_l_psindx = bfd_getl32 (eeom->psindx);
PRIV (eom_data).eom_l_tfradr = bfd_getl32 (eeom->tfradr);
abfd->start_address = PRIV (eom_data).eom_l_tfradr;
}
return TRUE;
}
/* Slurp an ordered set of VMS object records. Return FALSE on error. */
static bfd_boolean
_bfd_vms_slurp_object_records (bfd * abfd)
{
bfd_boolean err;
int type;
do
{
vms_debug2 ((7, "reading at %08lx\n", (unsigned long)bfd_tell (abfd)));
type = _bfd_vms_get_object_record (abfd);
if (type < 0)
{
vms_debug2 ((2, "next_record failed\n"));
return FALSE;
}
switch (type)
{
case EOBJ__C_EMH:
err = _bfd_vms_slurp_ehdr (abfd);
break;
case EOBJ__C_EEOM:
err = _bfd_vms_slurp_eeom (abfd);
break;
case EOBJ__C_EGSD:
err = _bfd_vms_slurp_egsd (abfd);
break;
case EOBJ__C_ETIR:
err = TRUE; /* _bfd_vms_slurp_etir (abfd); */
break;
case EOBJ__C_EDBG:
err = _bfd_vms_slurp_edbg (abfd);
break;
case EOBJ__C_ETBT:
err = _bfd_vms_slurp_etbt (abfd);
break;
default:
err = FALSE;
}
if (err != TRUE)
{
vms_debug2 ((2, "slurp type %d failed\n", type));
return FALSE;
}
}
while (type != EOBJ__C_EEOM);
return TRUE;
}
/* Initialize private data */
static bfd_boolean
vms_initialize (bfd * abfd)
{
bfd_size_type amt;
amt = sizeof (struct vms_private_data_struct);
abfd->tdata.any = bfd_zalloc (abfd, amt);
if (abfd->tdata.any == NULL)
return FALSE;
PRIV (recrd.file_format) = FF_UNKNOWN;
amt = sizeof (struct stack_struct) * STACKSIZE;
PRIV (stack) = bfd_alloc (abfd, amt);
if (PRIV (stack) == NULL)
goto error_ret1;
return TRUE;
error_ret1:
bfd_release (abfd, abfd->tdata.any);
abfd->tdata.any = NULL;
return FALSE;
}
/* Check the format for a file being read.
Return a (bfd_target *) if it's an object file or zero if not. */
static const struct bfd_target *
alpha_vms_object_p (bfd *abfd)
{
void *tdata_save = abfd->tdata.any;
unsigned int test_len;
unsigned char *buf;
vms_debug2 ((1, "vms_object_p(%p)\n", abfd));
/* Allocate alpha-vms specific data. */
if (!vms_initialize (abfd))
goto error_ret;
if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET))
goto err_wrong_format;
/* The first challenge with VMS is to discover the kind of the file.
Image files (executable or shared images) are stored as a raw
stream of bytes (like on UNIX), but there is no magic number.
Object files are written with RMS (record management service), ie
each records are preceeded by its length (on a word - 2 bytes), and
padded for word-alignment. That would be simple but when files
are transfered to a UNIX filesystem (using ftp), records are lost.
Only the raw content of the records are transfered. Fortunately,
the Alpha Object file format also store the length of the record
in the records. Is that clear ? */
/* Minimum is 6 bytes for objects (2 bytes size, 2 bytes record id,
2 bytes size repeated) and 12 bytes for images (4 bytes major id,
4 bytes minor id, 4 bytes length). */
test_len = 12;
/* Size the main buffer. */
buf = (unsigned char *) bfd_malloc (test_len);
if (buf == NULL)
goto error_ret;
PRIV (recrd.buf) = buf;
PRIV (recrd.buf_size) = test_len;
/* Initialize the record pointer. */
PRIV (recrd.rec) = buf;
if (bfd_bread (buf, test_len, abfd) != test_len)
goto err_wrong_format;
/* Is it an image? */
if ((bfd_getl32 (buf) == EIHD__K_MAJORID)
&& (bfd_getl32 (buf + 4) == EIHD__K_MINORID))
{
unsigned int to_read;
unsigned int read_so_far;
unsigned int remaining;
unsigned int eisd_offset, eihs_offset;
/* Extract the header size. */
PRIV (recrd.rec_size) = bfd_getl32 (buf + EIHD__L_SIZE);
/* The header size is 0 for DSF files. */
if (PRIV (recrd.rec_size) == 0)
PRIV (recrd.rec_size) = sizeof (struct vms_eihd);
if (PRIV (recrd.rec_size) > PRIV (recrd.buf_size))
{
buf = bfd_realloc_or_free (buf, PRIV (recrd.rec_size));
if (buf == NULL)
{
PRIV (recrd.buf) = NULL;
goto error_ret;
}
PRIV (recrd.buf) = buf;
PRIV (recrd.buf_size) = PRIV (recrd.rec_size);
}
/* Read the remaining record. */
remaining = PRIV (recrd.rec_size) - test_len;
to_read = MIN (VMS_BLOCK_SIZE - test_len, remaining);
read_so_far = test_len;
while (remaining > 0)
{
if (bfd_bread (buf + read_so_far, to_read, abfd) != to_read)
goto err_wrong_format;
read_so_far += to_read;
remaining -= to_read;
to_read = MIN (VMS_BLOCK_SIZE, remaining);
}
/* Reset the record pointer. */
PRIV (recrd.rec) = buf;
/* PR 17512: file: 7d7c57c2. */
if (PRIV (recrd.rec_size) < sizeof (struct vms_eihd))
goto error_ret;
vms_debug2 ((2, "file type is image\n"));
if (_bfd_vms_slurp_eihd (abfd, &eisd_offset, &eihs_offset) != TRUE)
goto err_wrong_format;
if (_bfd_vms_slurp_eisd (abfd, eisd_offset) != TRUE)
goto err_wrong_format;
/* EIHS is optional. */
if (eihs_offset != 0 && _bfd_vms_slurp_eihs (abfd, eihs_offset) != TRUE)
goto err_wrong_format;
}
else
{
int type;
/* Assume it's a module and adjust record pointer if necessary. */
maybe_adjust_record_pointer_for_object (abfd);
/* But is it really a module? */
if (bfd_getl16 (PRIV (recrd.rec)) <= EOBJ__C_MAXRECTYP
&& bfd_getl16 (PRIV (recrd.rec) + 2) <= EOBJ__C_MAXRECSIZ)
{
if (vms_get_remaining_object_record (abfd, test_len) <= 0)
goto err_wrong_format;
vms_debug2 ((2, "file type is module\n"));
type = bfd_getl16 (PRIV (recrd.rec));
if (type != EOBJ__C_EMH || _bfd_vms_slurp_ehdr (abfd) != TRUE)
goto err_wrong_format;
if (_bfd_vms_slurp_object_records (abfd) != TRUE)
goto err_wrong_format;
}
else
goto err_wrong_format;
}
/* Set arch_info to alpha. */
if (! bfd_default_set_arch_mach (abfd, bfd_arch_alpha, 0))
goto err_wrong_format;
return abfd->xvec;
err_wrong_format:
bfd_set_error (bfd_error_wrong_format);
error_ret:
if (PRIV (recrd.buf))
free (PRIV (recrd.buf));
if (abfd->tdata.any != tdata_save && abfd->tdata.any != NULL)
bfd_release (abfd, abfd->tdata.any);
abfd->tdata.any = tdata_save;
return NULL;
}
/* Image write. */
/* Write an EMH/MHD record. */
static void
_bfd_vms_write_emh (bfd *abfd)
{
struct vms_rec_wr *recwr = &PRIV (recwr);
_bfd_vms_output_alignment (recwr, 2);
/* EMH. */
_bfd_vms_output_begin (recwr, EOBJ__C_EMH);
_bfd_vms_output_short (recwr, EMH__C_MHD);
_bfd_vms_output_short (recwr, EOBJ__C_STRLVL);
_bfd_vms_output_long (recwr, 0);
_bfd_vms_output_long (recwr, 0);
_bfd_vms_output_long (recwr, MAX_OUTREC_SIZE);
/* Create module name from filename. */
if (bfd_get_filename (abfd) != 0)
{
char *module = vms_get_module_name (bfd_get_filename (abfd), TRUE);
_bfd_vms_output_counted (recwr, module);
free (module);
}
else
_bfd_vms_output_counted (recwr, "NONAME");
_bfd_vms_output_counted (recwr, BFD_VERSION_STRING);
_bfd_vms_output_dump (recwr, get_vms_time_string (), EMH_DATE_LENGTH);
_bfd_vms_output_fill (recwr, 0, EMH_DATE_LENGTH);
_bfd_vms_output_end (abfd, recwr);
}
/* Write an EMH/LMN record. */
static void
_bfd_vms_write_lmn (bfd *abfd, const char *name)
{
char version [64];
struct vms_rec_wr *recwr = &PRIV (recwr);
unsigned int ver = BFD_VERSION / 10000;
/* LMN. */
_bfd_vms_output_begin (recwr, EOBJ__C_EMH);
_bfd_vms_output_short (recwr, EMH__C_LNM);
snprintf (version, sizeof (version), "%s %d.%d.%d", name,
ver / 10000, (ver / 100) % 100, ver % 100);
_bfd_vms_output_dump (recwr, (unsigned char *)version, strlen (version));
_bfd_vms_output_end (abfd, recwr);
}
/* Write eom record for bfd abfd. Return FALSE on error. */
static bfd_boolean
_bfd_vms_write_eeom (bfd *abfd)
{
struct vms_rec_wr *recwr = &PRIV (recwr);
vms_debug2 ((2, "vms_write_eeom\n"));
_bfd_vms_output_alignment (recwr, 2);
_bfd_vms_output_begin (recwr, EOBJ__C_EEOM);
_bfd_vms_output_long (recwr, PRIV (vms_linkage_index + 1) >> 1);
_bfd_vms_output_byte (recwr, 0); /* Completion code. */
_bfd_vms_output_byte (recwr, 0); /* Fill byte. */
if ((abfd->flags & EXEC_P) == 0
&& bfd_get_start_address (abfd) != (bfd_vma)-1)
{
asection *section;
section = bfd_get_section_by_name (abfd, ".link");
if (section == 0)
{
bfd_set_error (bfd_error_nonrepresentable_section);
return FALSE;
}
_bfd_vms_output_short (recwr, 0);
_bfd_vms_output_long (recwr, (unsigned long) section->target_index);
_bfd_vms_output_long (recwr,
(unsigned long) bfd_get_start_address (abfd));
_bfd_vms_output_long (recwr, 0);
}
_bfd_vms_output_end (abfd, recwr);
return TRUE;
}
static void
vector_grow1 (struct vector_type *vec, size_t elsz)
{
if (vec->nbr_el + 1 < vec->max_el)
return;
if (vec->max_el == 0)
{
vec->max_el = 16;
vec->els = bfd_malloc2 (vec->max_el, elsz);
}
else
{
vec->max_el *= 2;
vec->els = bfd_realloc2 (vec->els, vec->max_el, elsz);
}
}
/* Bump ABFD file position to next block. */
static void
alpha_vms_file_position_block (bfd *abfd)
{
/* Next block. */
PRIV (file_pos) += VMS_BLOCK_SIZE - 1;
PRIV (file_pos) -= (PRIV (file_pos) % VMS_BLOCK_SIZE);
}
/* Convert from internal structure SRC to external structure DST. */
static void
alpha_vms_swap_eisd_out (struct vms_internal_eisd_map *src,
struct vms_eisd *dst)
{
bfd_putl32 (src->u.eisd.majorid, dst->majorid);
bfd_putl32 (src->u.eisd.minorid, dst->minorid);
bfd_putl32 (src->u.eisd.eisdsize, dst->eisdsize);
if (src->u.eisd.eisdsize <= EISD__K_LENEND)
return;
bfd_putl32 (src->u.eisd.secsize, dst->secsize);
bfd_putl64 (src->u.eisd.virt_addr, dst->virt_addr);
bfd_putl32 (src->u.eisd.flags, dst->flags);
bfd_putl32 (src->u.eisd.vbn, dst->vbn);
dst->pfc = src->u.eisd.pfc;
dst->matchctl = src->u.eisd.matchctl;
dst->type = src->u.eisd.type;
dst->fill_1 = 0;
if (src->u.eisd.flags & EISD__M_GBL)
{
bfd_putl32 (src->u.gbl_eisd.ident, dst->ident);
memcpy (dst->gblnam, src->u.gbl_eisd.gblnam,
src->u.gbl_eisd.gblnam[0] + 1);
}
}
/* Append EISD to the list of extra eisd for ABFD. */
static void
alpha_vms_append_extra_eisd (bfd *abfd, struct vms_internal_eisd_map *eisd)
{
eisd->next = NULL;
if (PRIV (gbl_eisd_head) == NULL)
PRIV (gbl_eisd_head) = eisd;
else
PRIV (gbl_eisd_tail)->next = eisd;
PRIV (gbl_eisd_tail) = eisd;
}
/* Create an EISD for shared image SHRIMG.
Return FALSE in case of error. */
static bfd_boolean
alpha_vms_create_eisd_for_shared (bfd *abfd, bfd *shrimg)
{
struct vms_internal_eisd_map *eisd;
int namlen;
namlen = strlen (PRIV2 (shrimg, hdr_data.hdr_t_name));
if (namlen + 5 > EISD__K_GBLNAMLEN)
{
/* Won't fit. */
return FALSE;
}
eisd = bfd_alloc (abfd, sizeof (*eisd));
if (eisd == NULL)
return FALSE;
/* Fill the fields. */
eisd->u.gbl_eisd.common.majorid = EISD__K_MAJORID;
eisd->u.gbl_eisd.common.minorid = EISD__K_MINORID;
eisd->u.gbl_eisd.common.eisdsize = (EISD__K_LEN + 4 + namlen + 5 + 3) & ~3;
eisd->u.gbl_eisd.common.secsize = VMS_BLOCK_SIZE; /* Must not be 0. */
eisd->u.gbl_eisd.common.virt_addr = 0;
eisd->u.gbl_eisd.common.flags = EISD__M_GBL;
eisd->u.gbl_eisd.common.vbn = 0;
eisd->u.gbl_eisd.common.pfc = 0;
eisd->u.gbl_eisd.common.matchctl = PRIV2 (shrimg, matchctl);
eisd->u.gbl_eisd.common.type = EISD__K_SHRPIC;
eisd->u.gbl_eisd.ident = PRIV2 (shrimg, ident);
eisd->u.gbl_eisd.gblnam[0] = namlen + 4;
memcpy (eisd->u.gbl_eisd.gblnam + 1, PRIV2 (shrimg, hdr_data.hdr_t_name),
namlen);
memcpy (eisd->u.gbl_eisd.gblnam + 1 + namlen, "_001", 4);
/* Append it to the list. */
alpha_vms_append_extra_eisd (abfd, eisd);
return TRUE;
}
/* Create an EISD for section SEC.
Return FALSE in case of failure. */
static bfd_boolean
alpha_vms_create_eisd_for_section (bfd *abfd, asection *sec)
{
struct vms_internal_eisd_map *eisd;
/* Only for allocating section. */
if (!(sec->flags & SEC_ALLOC))
return TRUE;
BFD_ASSERT (vms_section_data (sec)->eisd == NULL);
eisd = bfd_alloc (abfd, sizeof (*eisd));
if (eisd == NULL)
return