blob: df279bc4f68d06979c0218d08bafcca32bfcfad3 [file] [log] [blame]
/* vms.c -- BFD back-end for EVAX (openVMS/Alpha) files.
Copyright (C) 1996-2024 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 <limits.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))
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
/* 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;
bool 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 1, relocs have been read successfully, if 0 they have yet to be
read, if -1 reading relocs failed. */
int 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 bool _bfd_vms_slurp_object_records (bfd * abfd);
static bool alpha_vms_add_fixup_lp (struct bfd_link_info *, bfd *, bfd *);
static bool alpha_vms_add_fixup_ca (struct bfd_link_info *, bfd *, bfd *);
static bool alpha_vms_add_fixup_qr (struct bfd_link_info *, bfd *, bfd *,
bfd_vma);
static bool alpha_vms_add_fixup_lr (struct bfd_link_info *, unsigned int,
bfd_vma);
static bool alpha_vms_add_lw_reloc (struct bfd_link_info *);
static bool 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) \
((TYPE *) vector_grow1 (&VEC, sizeof (TYPE)))
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;
bool 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 bool
_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"));
/* PR 21813: Check for an undersized record. */
if (PRIV (recrd.buf_size) < sizeof (* eihd))
{
_bfd_error_handler (_("corrupt EIHD record - size is too small"));
bfd_set_error (bfd_error_bad_value);
return false;
}
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));
(void) size;
return true;
}
/* Read & process EISD record.
Return TRUE on success, FALSE on error. */
static bool
_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;
uint64_t 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)
|| (PRIV (recrd.rec_size) - offset
< offsetof (struct vms_eisd, eisdsize) + 4))
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;
}
/* Make sure that there is enough data present in the record. */
if (rec_size < offsetof (struct vms_eisd, type) + 1)
return false;
/* Make sure that the record is not too big either. */
if (rec_size > PRIV (recrd.rec_size) - offset)
return false;
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)
{
if (rec_size <= offsetof (struct vms_eisd, gblnam))
return false;
else if (rec_size < sizeof (struct vms_eisd))
name = _bfd_vms_save_counted_string (abfd, eisd->gblnam,
rec_size - offsetof (struct vms_eisd, gblnam));
else
name = _bfd_vms_save_counted_string (abfd, eisd->gblnam,
EISD__K_GBLNAMLEN);
if (name == NULL || name[0] == 0)
return false;
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 (name == NULL)
return false;
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 (section, bfd_flags))
return false;
}
return true;
}
/* Read & process EIHS record.
Return TRUE on success, FALSE on error. */
static bool
_bfd_vms_slurp_eihs (bfd *abfd, unsigned int offset)
{
unsigned char *p = PRIV (recrd.rec) + offset;
unsigned int gstvbn;
unsigned int gstsize ATTRIBUTE_UNUSED;
unsigned int dstvbn;
unsigned int dstsize;
unsigned int dmtvbn;
unsigned int dmtbytes;
asection *section;
/* PR 21611: Check that offset is valid. */
if (offset > PRIV (recrd.rec_size) - (EIHS__L_DMTBYTES + 4))
{
_bfd_error_handler (_("unable to read EIHS record at offset %#x"),
offset);
bfd_set_error (bfd_error_file_truncated);
return false;
}
gstvbn = bfd_getl32 (p + EIHS__L_GSTVBN);
gstsize = bfd_getl32 (p + EIHS__L_GSTSIZE);
dstvbn = bfd_getl32 (p + EIHS__L_DSTVBN);
dstsize = bfd_getl32 (p + EIHS__L_DSTSIZE);
dmtvbn = bfd_getl32 (p + EIHS__L_DMTVBN);
dmtbytes = bfd_getl32 (p + EIHS__L_DMTBYTES);
#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 (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 (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))
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_read (PRIV (recrd.buf), 1, abfd) != 1)
{
bfd_set_error (bfd_error_file_truncated);
return -1;
}
}
/* Read the record header */
if (bfd_read (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_or_free (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_read (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 bool
_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 (abfd, vms_rec + 20, 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 (abfd, ptr, *ptr);
ptr += *ptr + 1;
if (ptr + 17 >= end)
goto fail;
PRIV (hdr_data).hdr_t_date
= _bfd_vms_save_sized_string (abfd, 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 (abfd, 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 (abfd, 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 (abfd, 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 bool
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_or_free
(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, unsigned int max)
{
struct vms_symbol_entry *entry;
unsigned int len;
len = *ascic++;
max -= 1;
if (len > max)
{
_bfd_error_handler (_("record is too small for symbol name length"));
bfd_set_error (bfd_error_bad_value);
return NULL;
}
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 bool
_bfd_vms_slurp_egsd (bfd *abfd)
{
int gsd_type;
unsigned int gsd_size;
unsigned char *vms_rec;
bfd_vma base_addr;
long psindx;
vms_debug2 ((2, "EGSD\n"));
if (PRIV (recrd.rec_size) < 8)
{
_bfd_error_handler (_("corrupt EGSD record: its size (%#x) is too small"),
PRIV (recrd.rec_size));
bfd_set_error (bfd_error_bad_value);
return false;
}
PRIV (recrd.rec) += 8; /* Skip type, size, align pad. */
PRIV (recrd.rec_size) -= 8;
/* Calculate base address for each section. */
base_addr = 0;
while (PRIV (recrd.rec_size) > 4)
{
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));
/* PR 21615: Check for size overflow. */
if (PRIV (recrd.rec_size) < gsd_size)
{
_bfd_error_handler (_("corrupt EGSD record type %d: size (%#x) "
"is larger than remaining space (%#x)"),
gsd_type, gsd_size, PRIV (recrd.rec_size));
bfd_set_error (bfd_error_bad_value);
return false;
}
if (gsd_size < 4)
{
too_small:
_bfd_error_handler (_("corrupt EGSD record type %d: size (%#x) "
"is too small"),
gsd_type, gsd_size);
bfd_set_error (bfd_error_bad_value);
return false;
}
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;
if (offsetof (struct vms_egps, flags) + 2 > gsd_size)
goto too_small;
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;
bfd_vma align_addr;
size_t left;
if (offsetof (struct vms_egps, namlng) >= gsd_size)
goto too_small;
left = gsd_size - offsetof (struct vms_egps, namlng);
name = _bfd_vms_save_counted_string (abfd, &egps->namlng, left);
if (name == NULL || name[0] == 0)
return false;
section = bfd_make_section (abfd, name);
if (!section)
return false;
section->filepos = 0;
section->size = bfd_getl32 (egps->alloc);
section->alignment_power = egps->align & 31;
vms_section_data (section)->flags = vms_flags;
vms_section_data (section)->no_flags = 0;
new_flags = vms_secflag_by_name (evax_section_flags,
section->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 (section, new_flags))
return false;
/* Give a non-overlapping vma to non absolute sections. */
align_addr = (bfd_vma) 1 << section->alignment_power;
base_addr = (base_addr + align_addr - 1) & -align_addr;
section->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:
{
unsigned int nameoff;
struct vms_symbol_entry *entry;
struct vms_egsy *egsy = (struct vms_egsy *) vms_rec;
flagword old_flags;
if (offsetof (struct vms_egsy, flags) + 2 > gsd_size)
goto too_small;
old_flags = bfd_getl16 (egsy->flags);
if (old_flags & EGSY__V_DEF)
nameoff = ESDF__B_NAMLNG;
else
nameoff = ESRF__B_NAMLNG;
if (nameoff >= gsd_size)
goto too_small;
entry = add_symbol (abfd, vms_rec + nameoff, gsd_size - 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);
if (PRIV (sections) == NULL)
return false;
psindx = bfd_getl32 (esdf->psindx);
/* PR 21813: Check for an out of range index. */
if (psindx < 0 || psindx >= (int) PRIV (section_count))
{
bad_psindx:
_bfd_error_handler (_("corrupt EGSD record: its psindx "
"field is too big (%#lx)"),
psindx);
bfd_set_error (bfd_error_bad_value);
return false;
}
entry->section = PRIV (sections)[psindx];
if (old_flags & EGSY__V_NORM)
{
PRIV (norm_sym_count)++;
entry->code_value = bfd_getl64 (esdf->code_address);
psindx = bfd_getl32 (esdf->ca_psindx);
/* PR 21813: Check for an out of range index. */
if (psindx < 0 || psindx >= (int) PRIV (section_count))
goto bad_psindx;
entry->code_section = PRIV (sections)[psindx];
}
}
}
break;
case EGSD__C_SYMG:
{
struct vms_symbol_entry *entry;
struct vms_egst *egst = (struct vms_egst *)vms_rec;
flagword old_flags;
unsigned int nameoff = offsetof (struct vms_egst, namlng);
if (nameoff >= gsd_size)
goto too_small;
entry = add_symbol (abfd, &egst->namlng, gsd_size - nameoff);
if (entry == NULL)
return false;
old_flags = bfd_getl16 (egst->header.flags);
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)
{
if (PRIV (sections) == NULL)
return false;
psindx = bfd_getl32 (egst->psindx);
/* PR 21813: Check for an out of range index. */
if (psindx < 0 || psindx >= (int) PRIV (section_count))
goto bad_psindx;
entry->section = PRIV (sections)[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;
}
/* FIXME: Should we complain if PRIV (recrd.rec_size) is not zero ? */
if (PRIV (gsd_sym_count) > 0)
abfd->flags |= HAS_SYMS;
return true;
}
/* Stack routines for vms ETIR commands. */
/* Push value and section index. */
static bool
_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));
return false;
}
return true;
}
/* Pop value and section index. */
static bool
_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"));
return false;
}
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));
return true;
}
/* 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));
if (PRIV (sections) == NULL)
return;
if (sect < 0 || sect >= (int) PRIV (section_count))
return;
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 bool
dst_define_location (bfd *abfd, unsigned int loc)
{
vms_debug2 ((4, "dst_define_location (%d)\n", (int)loc));
if (loc > 1 << 24)
{
/* 16M entries ought to be plenty. */
bfd_set_error (bfd_error_bad_value);
_bfd_error_handler (_("dst_define_location %u too large"), loc);
return false;
}
/* Grow the ptr offset table if necessary. */
if (loc + 1 > PRIV (dst_ptr_offsets_count))
{
PRIV (dst_ptr_offsets)
= bfd_realloc_or_free (PRIV (dst_ptr_offsets),
(loc + 1) * sizeof (unsigned int));
if (PRIV (dst_ptr_offsets) == NULL)
return false;
memset (PRIV (dst_ptr_offsets) + PRIV (dst_ptr_offsets_count), 0,
(loc - PRIV (dst_ptr_offsets_count)) * sizeof (unsigned int));
PRIV (dst_ptr_offsets_count) = loc + 1;
}
PRIV (dst_ptr_offsets)[loc] = PRIV (image_offset);
return true;
}
/* Restore saved DST location counter from specified index. */
static bool
dst_restore_location (bfd *abfd, unsigned int loc)
{
vms_debug2 ((4, "dst_restore_location (%d)\n", (int)loc));
if (loc < PRIV (dst_ptr_offsets_count))
{
PRIV (image_offset) = PRIV (dst_ptr_offsets)[loc];
return true;
}
return false;
}
/* Retrieve saved DST location counter from specified index. */
static bool
dst_retrieve_location (bfd *abfd, bfd_vma *loc)
{
vms_debug2 ((4, "dst_retrieve_location (%d)\n", (int) *loc));
if (*loc < PRIV (dst_ptr_offsets_count))
{
*loc = PRIV (dst_ptr_offsets)[*loc];
return true;
}
return false;
}
/* Write multiple bytes to section image. */
static bool
image_write (bfd *abfd, unsigned char *ptr, unsigned int size)
{
asection *sec = PRIV (image_section);
size_t off = PRIV (image_offset);
/* Check bounds. */
if (off > sec->size
|| size > sec->size - off)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
#if VMS_DEBUG
_bfd_vms_debug (8, "image_write from (%p, %d) to (%ld)\n", ptr, size,
(long) off);
#endif
if (PRIV (image_section)->contents != NULL)
memcpy (sec->contents + off, ptr, size);
else
{
unsigned int i;
for (i = 0; i < size; i++)
if (ptr[i] != 0)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
}
#if VMS_DEBUG
_bfd_hexdump (9, ptr, size, 0);
#endif
PRIV (image_offset) += size;
return true;
}
/* Write byte to section image. */
static bool
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 bool
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 bool
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 bool
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,
const unsigned char *max_ascic,
struct bfd_link_info *info,
bfd_vma *vma,
struct alpha_vms_link_hash_entry **hp)
{
char name[257];
unsigned int len;
unsigned 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;
if (ascic + len >= max_ascic)
{
_bfd_error_handler (_("corrupt vms value"));
*vma = 0;
*hp = NULL;
return;
}
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;
if (PRIV (sections) == NULL)
return 0;
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 bool
_bfd_vms_slurp_etir (bfd *abfd, struct bfd_link_info *info)
{
unsigned char *ptr;
unsigned int length;
unsigned char *maxptr;
bfd_vma op1 = 0;
bfd_vma op2 = 0;
unsigned int rel1 = RELC_NONE;
unsigned int rel2 = RELC_NONE;
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, cmd_length;
if (ptr + 4 > maxptr)
goto corrupt_etir;
cmd = bfd_getl16 (ptr);
cmd_length = bfd_getl16 (ptr + 2);
/* PR 21589 and 21579: Check for a corrupt ETIR record. */
if (cmd_length < 4 || ptr + cmd_length > maxptr)
{
corrupt_etir:
_bfd_error_handler (_("corrupt ETIR record encountered"));
bfd_set_error (bfd_error_bad_value);
return false;
}
ptr += 4;
cmd_length -= 4;
#if VMS_DEBUG
_bfd_vms_debug (4, "etir: %s(%d)\n",
_bfd_vms_etir_name (cmd), cmd);
_bfd_hexdump (8, ptr, cmd_length, 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, ptr + cmd_length, info, &op1, &h);
if (!_bfd_vms_push (abfd, op1, alpha_vms_sym_to_ctxt (h)))
return false;
break;
/* Stack longword
arg: lw value
stack 32 bit value, sign extend to 64 bit. */
case ETIR__C_STA_LW:
if (cmd_length < 4)
goto corrupt_etir;
if (!_bfd_vms_push (abfd, bfd_getl32 (ptr), RELC_NONE))
return false;
break;
/* Stack quadword
arg: qw value
stack 64 bit value of symbol. */
case ETIR__C_STA_QW:
if (cmd_length < 8)
goto corrupt_etir;
if (!_bfd_vms_push (abfd, bfd_getl64 (ptr), RELC_NONE))
return false;
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;
if (cmd_length < 12)
goto corrupt_etir;
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);
if (!_bfd_vms_push (abfd, op1, psect | RELC_SEC_BASE))
return false;
}
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:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!image_write_b (abfd, (unsigned int) op1 & 0xff))
return false;
break;
/* Store word: pop stack, write word
arg: -. */
case ETIR__C_STO_W:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!image_write_w (abfd, (unsigned int) op1 & 0xffff))
return false;
break;
/* Store longword: pop stack, write longword
arg: -. */
case ETIR__C_STO_LW:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 & RELC_SEC_BASE)
{
op1 = alpha_vms_fix_sec_rel (abfd, info, rel1, op1);
rel1 = RELC_REL;
}
else if (rel1 & RELC_SHR_BASE)
{
if (!alpha_vms_add_fixup_lr (info, rel1 & RELC_MASK, op1))
return false;
rel1 = RELC_NONE;
}
if (rel1 != RELC_NONE)
{
if (rel1 != RELC_REL)
abort ();
if (!alpha_vms_add_lw_reloc (info))
return false;
}
if (!image_write_l (abfd, op1))
return false;
break;
/* Store quadword: pop stack, write quadword
arg: -. */
case ETIR__C_STO_QW:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
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 ();
if (!alpha_vms_add_qw_reloc (info))
return false;
}
if (!image_write_q (abfd, op1))
return false;
break;
/* Store immediate repeated: pop stack for repeat count
arg: lw byte count
da data. */
case ETIR__C_STO_IMMR:
{
int size;
if (cmd_length < 4)
goto corrupt_etir;
size = bfd_getl32 (ptr);
if (size > cmd_length - 4)
goto corrupt_etir;
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (size == 0)
break;
op1 &= 0xffffffff;
while (op1-- > 0)
if (!image_write (abfd, ptr + 4, size))
return false;
}
break;
/* Store global: write symbol value
arg: cs global symbol name. */
case ETIR__C_STO_GBL:
_bfd_vms_get_value (abfd, ptr, ptr + cmd_length, info, &op1, &h);
if (h && h->sym)
{
if (h->sym->typ == EGSD__C_SYMG)
{
if (!alpha_vms_add_fixup_qr (info, abfd, h->sym->owner,
h->sym->symbol_vector))
return false;
op1 = 0;
}
else
{
op1 = alpha_vms_get_sym_value (h->sym->section,
h->sym->value);
if (!alpha_vms_add_qw_reloc (info))
return false;
}
}
if (!image_write_q (abfd, op1))
return false;
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, ptr + cmd_length, 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)
{
if (!alpha_vms_add_fixup_ca (info, abfd, h->sym->owner))
return false;
op1 = h->sym->symbol_vector;
}
else
{
op1 = alpha_vms_get_sym_value (h->sym->code_section,
h->sym->code_value);
if (!alpha_vms_add_qw_reloc (info))
return false;
}
}
else
{
/* Symbol is not a procedure. */
abort ();
}
}
if (!image_write_q (abfd, op1))
return false;
break;
/* Store offset to psect: pop stack, add low 32 bits to base of psect
arg: none. */
case ETIR__C_STO_OFF:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (!(rel1 & RELC_SEC_BASE))
abort ();
op1 = alpha_vms_fix_sec_rel (abfd, info, rel1, op1);
rel1 = RELC_REL;
if (!image_write_q (abfd, op1))
return false;
break;
/* Store immediate
arg: lw count of bytes
da data. */
case ETIR__C_STO_IMM:
{
unsigned int size;
if (cmd_length < 4)
goto corrupt_etir;
size = bfd_getl32 (ptr);
if (!image_write (abfd, ptr + 4, size))
return false;
}
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, ptr + cmd_length, info, &op1, &h);
#if 0
abort ();
#endif
if (!image_write_l (abfd, op1))
return false;
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:
if (cmd_length < 4)
goto corrupt_etir;
_bfd_vms_get_value (abfd, ptr + 4, ptr + cmd_length, info, &op1, &h);
if (h && h->sym)
{
if (h->sym->typ == EGSD__C_SYMG)
{
if (!alpha_vms_add_fixup_lp (info, abfd, h->sym->owner))
return false;
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;
}
if (!image_write_q (abfd, op1)
|| !image_write_q (abfd, op2))
return false;
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:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
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:
if (cmd_length < 4)
goto corrupt_etir;
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:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!dst_define_location (abfd, op1))
return false;
break;
/* Set location: pop index, restore location counter from index
arg: none. */
case ETIR__C_CTL_STLOC:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!dst_restore_location (abfd, op1))
{
bfd_set_error (bfd_error_bad_value);
_bfd_error_handler (_("invalid %s"), "ETIR__C_CTL_STLOC");
return false;
}
break;
/* Stack defined location: pop index, push location counter from index
arg: none. */
case ETIR__C_CTL_STKDL:
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!dst_retrieve_location (abfd, &op1))
{
bfd_set_error (bfd_error_bad_value);
_bfd_error_handler (_("invalid %s"), "ETIR__C_CTL_STKDL");
return false;
}
if (!_bfd_vms_push (abfd, op1, RELC_NONE))
return false;
break;
case ETIR__C_OPR_NOP: /* No-op. */
break;
case ETIR__C_OPR_ADD: /* Add. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (rel1 == RELC_NONE && rel2 != RELC_NONE)
rel1 = rel2;
else if (rel1 != RELC_NONE && rel2 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, op1 + op2, rel1))
return false;
break;
case ETIR__C_OPR_SUB: /* Subtract. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
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;
if (!_bfd_vms_push (abfd, op2 - op1, rel1))
return false;
break;
case ETIR__C_OPR_MUL: /* Multiply. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, op1 * op2, RELC_NONE))
return false;
break;
case ETIR__C_OPR_DIV: /* Divide. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
if (op1 == 0)
{
/* Divide by zero is supposed to give a result of zero,
and a non-fatal warning message. */
_bfd_error_handler (_("%s divide by zero"), "ETIR__C_OPR_DIV");
if (!_bfd_vms_push (abfd, 0, RELC_NONE))
return false;
}
else
{
if (!_bfd_vms_push (abfd, op2 / op1, RELC_NONE))
return false;
}
break;
case ETIR__C_OPR_AND: /* Logical AND. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, op1 & op2, RELC_NONE))
return false;
break;
case ETIR__C_OPR_IOR: /* Logical inclusive OR. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, op1 | op2, RELC_NONE))
return false;
break;
case ETIR__C_OPR_EOR: /* Logical exclusive OR. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (rel1 != RELC_NONE || rel2 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, op1 ^ op2, RELC_NONE))
return false;
break;
case ETIR__C_OPR_NEG: /* Negate. */
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, -op1, RELC_NONE))
return false;
break;
case ETIR__C_OPR_COM: /* Complement. */
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (rel1 != RELC_NONE)
goto bad_context;
if (!_bfd_vms_push (abfd, ~op1, RELC_NONE))
return false;
break;
case ETIR__C_OPR_ASH: /* Arithmetic shift. */
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
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 ((bfd_signed_vma) op2 < 0)
{
/* Shift right. */
bfd_vma sign;
op2 = -op2;
if (op2 >= CHAR_BIT * sizeof (op1))
op2 = CHAR_BIT * sizeof (op1) - 1;
/* op1 = (bfd_signed_vma) op1 >> op2; */
sign = op1 & ((bfd_vma) 1 << (CHAR_BIT * sizeof (op1) - 1));
op1 >>= op2;
sign >>= op2;
op1 = (op1 ^ sign) - sign;
}
else
{
/* Shift left. */
if (op2 >= CHAR_BIT * sizeof (op1))
op1 = 0;
else
op1 <<= op2;
}
if (!_bfd_vms_push (abfd, op1, RELC_NONE)) /* FIXME: sym. */
return false;
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. */
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
if (op1 & 0x01L)
{
if (!_bfd_vms_pop (abfd, &op1, &rel1))
return false;
}
else
{
if (!_bfd_vms_pop (abfd, &op1, &rel1)
|| !_bfd_vms_pop (abfd, &op2, &rel2))
return false;
if (!_bfd_vms_push (abfd, op1, rel1))
return false;
}
break;
default:
_bfd_error_handler (_("reserved cmd %d"), cmd);
return false;
break;
}
ptr += cmd_length;
}
return true;
}
/* Process EDBG/ETBT record.
Return TRUE on success, FALSE on error */
static bool
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 (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 bool
_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 bool
_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 bool
_bfd_vms_slurp_eeom (bfd *abfd)
{
struct vms_eeom *eeom = (struct vms_eeom *) PRIV (recrd.rec);
vms_debug2 ((2, "EEOM\n"));
/* PR 21813: Check for an undersized record. */
if (PRIV (recrd.buf_size) < sizeof (* eeom))
{
_bfd_error_handler (_("corrupt EEOM record - size is too small"));
bfd_set_error (bfd_error_bad_value);
return false;
}
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 !"));
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 bool
_bfd_vms_slurp_object_records (bfd * abfd)
{
bool ok;
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:
ok = _bfd_vms_slurp_ehdr (abfd);
break;
case EOBJ__C_EEOM:
ok = _bfd_vms_slurp_eeom (abfd);
break;
case EOBJ__C_EGSD:
ok = _bfd_vms_slurp_egsd (abfd);
break;
case EOBJ__C_ETIR:
ok = true; /* _bfd_vms_slurp_etir (abfd); */
break;
case EOBJ__C_EDBG:
ok = _bfd_vms_slurp_edbg (abfd);
break;
case EOBJ__C_ETBT:
ok = _bfd_vms_slurp_etbt (abfd);
break;
default:
ok = false;
}
if (!ok)
{
vms_debug2 ((2, "slurp type %d failed\n", type));
return false;
}
}
while (type != EOBJ__C_EEOM);
return true;
}
/* Initialize private data */
static bool
vms_initialize (bfd * abfd)
{
size_t 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;
}
/* Free malloc'd memory. */
static void
alpha_vms_free_private (bfd *abfd)
{
struct module *module;
free (PRIV (recrd.buf));
free (PRIV (sections));
free (PRIV (syms));
free (PRIV (dst_ptr_offsets));
for (module = PRIV (modules); module; module = module->next)
free (module->file_table);
}
/* Check the format for a file being read.
Return a (bfd_target *) if it's an object file or zero if not. */
static bfd_cleanup
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))
{
abfd->tdata.any = tdata_save;
return NULL;
}
if (bfd_seek (abfd, 0, SEEK_SET))
goto error_ret;
/* 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;
buf = _bfd_malloc_and_read (abfd, test_len, test_len);
if (buf == NULL)
goto error_ret;
PRIV (recrd.buf) = buf;
PRIV (recrd.buf_size) = test_len;
PRIV (recrd.rec) = buf;
/* Is it an image? */
if ((bfd_getl32 (buf) == EIHD__K_MAJORID)
&& (bfd_getl32 (buf + 4) == EIHD__K_MINORID))
{
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);
/* PR 21813: Check for a truncated record. */
/* PR 17512: file: 7d7c57c2. */
if (PRIV (recrd.rec_size) < sizeof (struct vms_eihd))
goto err_wrong_format;
if (bfd_seek (abfd, 0, SEEK_SET))
goto error_ret;
free (PRIV (recrd.buf));
PRIV (recrd.buf) = NULL;
buf = _bfd_malloc_and_read (abfd, PRIV (recrd.rec_size),
PRIV (recrd.rec_size));
if (buf == NULL)
goto error_ret;
PRIV (recrd.buf) = buf;
PRIV (recrd.buf_size) = PRIV (recrd.rec_size);
PRIV (recrd.rec) = buf;
vms_debug2 ((2, "file type is image\n"));
if (!_bfd_vms_slurp_eihd (abfd, &eisd_offset, &eihs_offset))
goto err_wrong_format;
if (!_bfd_vms_slurp_eisd (abfd, eisd_offset))
goto err_wrong_format;
/* EIHS is optional. */
if (eihs_offset != 0 && !_bfd_vms_slurp_eihs (abfd, eihs_offset))
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))
goto err_wrong_format;
if (!_bfd_vms_slurp_object_records (abfd))
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 alpha_vms_free_private;
err_wrong_format:
bfd_set_error (bfd_error_wrong_format);
error_ret:
alpha_vms_free_private (abfd);
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 bool
_bfd_vms_write_emh (bfd *abfd)
{
struct vms_rec_wr *recwr = &PRIV (recwr);
unsigned char tbuf[18];
_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 (tbuf), EMH_DATE_LENGTH);
_bfd_vms_output_fill (recwr, 0, EMH_DATE_LENGTH);
return _bfd_vms_output_end (abfd, recwr);
}
/* Write an EMH/LMN record. */
static bool
_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));
return _bfd_vms_output_end (abfd, recwr);
}
/* Write eom record for bfd abfd. Return FALSE on error. */
static bool
_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);
}
return _bfd_vms_output_end (abfd, recwr);
}
static void *
vector_grow1 (struct vector_type *vec, size_t elsz)
{
if (vec->nbr_el >= vec->max_el)
{
if (vec->max_el == 0)
{
vec->max_el = 16;
vec->els = bfd_malloc (vec->max_el * elsz);
}
else
{
size_t amt;
if (vec->max_el > -1u / 2)
{
bfd_set_error (bfd_error_file_too_big);
return NULL;
}
vec->max_el *= 2;
if (_bfd_mul_overflow (vec->max_el, elsz, &amt))
{
bfd_set_error (bfd_error_file_too_big);
return NULL;
}
vec->els = bfd_realloc_or_free (vec->els, amt);
}
}
if (vec->els == NULL)
return NULL;
return (char *) vec->els + elsz * vec->nbr_el++;
}
/* 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 bool
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. */