Coverage Report

Created: 2025-06-24 06:45

/src/binutils-gdb/bfd/vms-misc.c
Line
Count
Source (jump to first uncovered line)
1
/* vms-misc.c -- BFD back-end for VMS/VAX (openVMS/VAX) and
2
   EVAX (openVMS/Alpha) files.
3
   Copyright (C) 1996-2025 Free Software Foundation, Inc.
4
5
   Miscellaneous functions.
6
7
   Written by Klaus K"ampf (kkaempf@rmi.de)
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program; if not, write to the Free Software
21
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22
   MA 02110-1301, USA.  */
23
24
#if __STDC__
25
#include <stdarg.h>
26
#endif
27
28
#include "sysdep.h"
29
#include "bfd.h"
30
#include "bfdlink.h"
31
#include "libbfd.h"
32
#include "safe-ctype.h"
33
34
#ifdef VMS
35
#define __NEW_STARLET
36
#include <rms.h>
37
#include <unixlib.h>
38
#include <gen64def.h>
39
#include <starlet.h>
40
#define RME$C_SETRFM 0x00000001
41
#include <unistd.h>
42
#endif
43
#include <time.h>
44
45
#include "vms.h"
46
#include "vms/emh.h"
47
48
#if VMS_DEBUG
49
/* Debug functions.  */
50
51
/* Debug function for all vms extensions evaluates environment
52
   variable VMS_DEBUG for a numerical value on the first call all
53
   error levels below this value are printed:
54
55
   Levels:
56
   1  toplevel bfd calls (functions from the bfd vector)
57
   2  functions called by bfd calls
58
   ...
59
   9  almost everything
60
61
   Level is also indentation level. Indentation is performed
62
   if level > 0.  */
63
64
void
65
_bfd_vms_debug (int level, char *format, ...)
66
{
67
  static int min_level = -1;
68
  static FILE *output = NULL;
69
  char *eptr;
70
  va_list args;
71
  int abslvl = (level > 0) ? level : - level;
72
73
  if (min_level == -1)
74
    {
75
      if ((eptr = getenv ("VMS_DEBUG")) != NULL)
76
  {
77
    min_level = atoi (eptr);
78
    output = stderr;
79
  }
80
      else
81
  min_level = 0;
82
    }
83
  if (output == NULL)
84
    return;
85
  if (abslvl > min_level)
86
    return;
87
88
  while (--level > 0)
89
    fprintf (output, " ");
90
  va_start (args, format);
91
  vfprintf (output, format, args);
92
  fflush (output);
93
  va_end (args);
94
}
95
96
/* A debug function
97
   hex dump 'size' bytes starting at 'ptr'.  */
98
99
void
100
_bfd_hexdump (int level, unsigned char *ptr, int size, int offset)
101
{
102
  unsigned char *lptr = ptr;
103
  int count = 0;
104
  long start = offset;
105
106
  while (size-- > 0)
107
    {
108
      if ((count % 16) == 0)
109
  vms_debug (level, "%08lx:", start);
110
      vms_debug (-level, " %02x", *ptr++);
111
      count++;
112
      start++;
113
      if (size == 0)
114
  {
115
    while ((count % 16) != 0)
116
      {
117
        vms_debug (-level, "   ");
118
        count++;
119
      }
120
  }
121
      if ((count % 16) == 0)
122
  {
123
    vms_debug (-level, " ");
124
    while (lptr < ptr)
125
      {
126
        vms_debug (-level, "%c", (*lptr < 32) ? '.' : *lptr);
127
        lptr++;
128
      }
129
    vms_debug (-level, "\n");
130
  }
131
    }
132
  if ((count % 16) != 0)
133
    vms_debug (-level, "\n");
134
}
135
#endif
136

137
138
/* Copy sized string (string with fixed size) to new allocated area.
139
   Size is string size (size of record).  */
140
141
char *
142
_bfd_vms_save_sized_string (bfd *abfd, unsigned char *str, size_t size)
143
111k
{
144
111k
  char *newstr;
145
146
111k
  if (size == (size_t) -1)
147
0
    {
148
0
      bfd_set_error (bfd_error_no_memory);
149
0
      return NULL;
150
0
    }
151
111k
  newstr = bfd_alloc (abfd, size + 1);
152
111k
  if (newstr == NULL)
153
0
    return NULL;
154
111k
  memcpy (newstr, str, size);
155
111k
  newstr[size] = 0;
156
157
111k
  return newstr;
158
111k
}
159
160
/* Copy counted string (string with size at first byte) to new allocated area.
161
   PTR points to size byte on entry.  */
162
163
char *
164
_bfd_vms_save_counted_string (bfd *abfd, unsigned char *ptr, size_t maxlen)
165
108k
{
166
108k
  unsigned int len;
167
168
108k
  if (maxlen == 0)
169
71.6k
    return NULL;
170
36.4k
  len = *ptr++;
171
36.4k
  if (len >  maxlen - 1)
172
24.6k
    return NULL;
173
11.8k
  return _bfd_vms_save_sized_string (abfd, ptr, len);
174
36.4k
}
175

176
/* Object output routines.   */
177
178
/* Begin new record.
179
   Write 2 bytes rectype and 2 bytes record length.  */
180
181
void
182
_bfd_vms_output_begin (struct vms_rec_wr *recwr, int rectype)
183
0
{
184
0
  vms_debug2 ((6, "_bfd_vms_output_begin (type %d)\n", rectype));
185
186
  /* Record must have been closed.  */
187
0
  BFD_ASSERT (recwr->size == 0);
188
189
0
  _bfd_vms_output_short (recwr, (unsigned int) rectype);
190
191
  /* Placeholder for length.  */
192
0
  _bfd_vms_output_short (recwr, 0);
193
0
}
194
195
/* Begin new sub-record.
196
   Write 2 bytes rectype, and 2 bytes record length.  */
197
198
void
199
_bfd_vms_output_begin_subrec (struct vms_rec_wr *recwr, int rectype)
200
0
{
201
0
  vms_debug2 ((6, "_bfd_vms_output_begin_subrec (type %d)\n", rectype));
202
203
  /* Subrecord must have been closed.  */
204
0
  BFD_ASSERT (recwr->subrec_offset == 0);
205
206
  /* Save start of subrecord offset.  */
207
0
  recwr->subrec_offset = recwr->size;
208
209
  /* Subrecord type.  */
210
0
  _bfd_vms_output_short (recwr, (unsigned int) rectype);
211
212
  /* Placeholder for length.  */
213
0
  _bfd_vms_output_short (recwr, 0);
214
0
}
215
216
/* Set record/subrecord alignment.   */
217
218
void
219
_bfd_vms_output_alignment (struct vms_rec_wr *recwr, int alignto)
220
0
{
221
0
  vms_debug2 ((6, "_bfd_vms_output_alignment (%d)\n", alignto));
222
0
  recwr->align = alignto;
223
0
}
224
225
/* Align the size of the current record (whose length is LENGTH).
226
   Warning: this obviously changes the record (and the possible subrecord)
227
   length.  */
228
229
static void
230
_bfd_vms_output_align (struct vms_rec_wr *recwr, unsigned int length)
231
0
{
232
0
  unsigned int real_size = recwr->size;
233
0
  unsigned int aligncount;
234
235
  /* Pad with 0 if alignment is required.  */
236
0
  aligncount = (recwr->align - (length % recwr->align)) % recwr->align;
237
0
  vms_debug2 ((6, "align: adding %d bytes\n", aligncount));
238
0
  while (aligncount-- > 0)
239
0
    recwr->buf[real_size++] = 0;
240
241
0
  recwr->size = real_size;
242
0
}
243
244
/* Ends current sub-record.  Set length field.  */
245
246
void
247
_bfd_vms_output_end_subrec (struct vms_rec_wr *recwr)
248
0
{
249
0
  int real_size = recwr->size;
250
0
  int length;
251
252
  /* Subrecord must be open.  */
253
0
  BFD_ASSERT (recwr->subrec_offset != 0);
254
255
0
  length = real_size - recwr->subrec_offset;
256
257
0
  if (length == 0)
258
0
    return;
259
260
0
  _bfd_vms_output_align (recwr, length);
261
262
  /* Put length to buffer.  */
263
0
  bfd_putl16 ((bfd_vma) (recwr->size - recwr->subrec_offset),
264
0
        recwr->buf + recwr->subrec_offset + 2);
265
266
  /* Close the subrecord.  */
267
0
  recwr->subrec_offset = 0;
268
0
}
269
270
/* Ends current record (and write it).  */
271
272
bool
273
_bfd_vms_output_end (bfd *abfd, struct vms_rec_wr *recwr)
274
0
{
275
0
  vms_debug2 ((6, "_bfd_vms_output_end (size %u)\n", recwr->size));
276
277
  /* Subrecord must have been closed.  */
278
0
  BFD_ASSERT (recwr->subrec_offset == 0);
279
280
0
  if (recwr->size == 0)
281
0
    return true;
282
283
0
  _bfd_vms_output_align (recwr, recwr->size);
284
285
  /* Write the length word.  */
286
0
  bfd_putl16 ((bfd_vma) recwr->size, recwr->buf + 2);
287
288
  /* File is open in undefined (UDF) format on VMS, but ultimately will be
289
     converted to variable length (VAR) format.  VAR format has a length
290
     word first which must be explicitly output in UDF format.  */
291
  /* So, first the length word.  */
292
0
  if (bfd_write (recwr->buf + 2, 2, abfd) != 2)
293
0
    return false;
294
295
  /* Align.  */
296
0
  if (recwr->size & 1)
297
0
    recwr->buf[recwr->size++] = 0;
298
299
  /* Then the record.  */
300
0
  if (bfd_write (recwr->buf, (size_t) recwr->size, abfd)
301
0
      != (size_t) recwr->size)
302
0
    return false;
303
304
0
  recwr->size = 0;
305
0
  return true;
306
0
}
307
308
/* Check remaining buffer size.  Return what's left.  */
309
310
int
311
_bfd_vms_output_check (struct vms_rec_wr *recwr, int size)
312
0
{
313
0
  vms_debug2 ((6, "_bfd_vms_output_check (%d)\n", size));
314
315
0
  return (MAX_OUTREC_SIZE - (recwr->size + size + MIN_OUTREC_LUFT));
316
0
}
317
318
/* Output byte (8 bit) value.  */
319
320
void
321
_bfd_vms_output_byte (struct vms_rec_wr *recwr, unsigned int value)
322
0
{
323
0
  vms_debug2 ((6, "_bfd_vms_output_byte (%02x)\n", value));
324
325
0
  *(recwr->buf + recwr->size) = value;
326
0
  recwr->size += 1;
327
0
}
328
329
/* Output short (16 bit) value.  */
330
331
void
332
_bfd_vms_output_short (struct vms_rec_wr *recwr, unsigned int value)
333
0
{
334
0
  vms_debug2 ((6, "_bfd_vms_output_short (%04x)\n", value));
335
336
0
  bfd_putl16 ((bfd_vma) value & 0xffff, recwr->buf + recwr->size);
337
0
  recwr->size += 2;
338
0
}
339
340
/* Output long (32 bit) value.  */
341
342
void
343
_bfd_vms_output_long (struct vms_rec_wr *recwr, unsigned long value)
344
0
{
345
0
  vms_debug2 ((6, "_bfd_vms_output_long (%08lx)\n", value));
346
347
0
  bfd_putl32 ((bfd_vma) value, recwr->buf + recwr->size);
348
0
  recwr->size += 4;
349
0
}
350
351
/* Output quad (64 bit) value.  */
352
353
void
354
_bfd_vms_output_quad (struct vms_rec_wr *recwr, bfd_vma value)
355
0
{
356
0
  vms_debug2 ((6, "_bfd_vms_output_quad (%08lx)\n", (unsigned long)value));
357
358
0
  bfd_putl64 (value, recwr->buf + recwr->size);
359
0
  recwr->size += 8;
360
0
}
361
362
/* Output c-string as counted string.  */
363
364
void
365
_bfd_vms_output_counted (struct vms_rec_wr *recwr, const char *value)
366
0
{
367
0
  int len;
368
369
0
  vms_debug2 ((6, "_bfd_vms_output_counted (%s)\n", value));
370
371
0
  len = strlen (value);
372
0
  if (len == 0)
373
0
    {
374
0
      _bfd_error_handler (_("_bfd_vms_output_counted called with zero bytes"));
375
0
      return;
376
0
    }
377
0
  if (len > 255)
378
0
    {
379
0
      _bfd_error_handler (_("_bfd_vms_output_counted called with too many bytes"));
380
0
      return;
381
0
    }
382
0
  _bfd_vms_output_byte (recwr, (unsigned int) len & 0xff);
383
0
  _bfd_vms_output_dump (recwr, (const unsigned char *)value, len);
384
0
}
385
386
/* Output character area.  */
387
388
void
389
_bfd_vms_output_dump (struct vms_rec_wr *recwr, const unsigned char *data, int len)
390
0
{
391
0
  vms_debug2 ((6, "_bfd_vms_output_dump (%d)\n", len));
392
393
0
  if (len == 0)
394
0
    return;
395
396
0
  memcpy (recwr->buf + recwr->size, data, (size_t) len);
397
0
  recwr->size += len;
398
0
}
399
400
/* Output count bytes of value.  */
401
402
void
403
_bfd_vms_output_fill (struct vms_rec_wr *recwr, int value, int count)
404
0
{
405
0
  vms_debug2 ((6, "_bfd_vms_output_fill (val %02x times %d)\n", value, count));
406
407
0
  if (count == 0)
408
0
    return;
409
0
  memset (recwr->buf + recwr->size, value, (size_t) count);
410
0
  recwr->size += count;
411
0
}
412
413
#ifdef VMS
414
/* Convert the file to variable record length format. This is done
415
   using undocumented system call sys$modify().
416
   Pure VMS version.  */
417
418
static void
419
vms_convert_to_var (char * vms_filename)
420
{
421
  struct FAB fab = cc$rms_fab;
422
423
  fab.fab$l_fna = vms_filename;
424
  fab.fab$b_fns = strlen (vms_filename);
425
  fab.fab$b_fac = FAB$M_PUT;
426
  fab.fab$l_fop = FAB$M_ESC;
427
  fab.fab$l_ctx = RME$C_SETRFM;
428
429
  sys$open (&fab);
430
431
  fab.fab$b_rfm = FAB$C_VAR;
432
433
  sys$modify (&fab);
434
  sys$close (&fab);
435
}
436
437
static int
438
vms_convert_to_var_1 (char *filename, int type)
439
{
440
  if (type != DECC$K_FILE)
441
    return false;
442
  vms_convert_to_var (filename);
443
  return true;
444
}
445
446
/* Convert the file to variable record length format. This is done
447
   using undocumented system call sys$modify().
448
   Unix filename version.  */
449
450
int
451
_bfd_vms_convert_to_var_unix_filename (const char *unix_filename)
452
{
453
  if (decc$to_vms (unix_filename, &vms_convert_to_var_1, 0, 1) != 1)
454
    return false;
455
  return true;
456
}
457
#endif /* VMS */
458
459
/* Manufacture a VMS like time on a unix based system.
460
   stolen from obj-vms.c.  */
461
462
unsigned char *
463
get_vms_time_string (unsigned char *tbuf)
464
0
{
465
0
#ifndef VMS
466
0
  char *pnt;
467
0
  time_t timeb;
468
469
0
  time (& timeb);
470
0
  pnt = ctime (&timeb);
471
0
  pnt[3] = 0;
472
0
  pnt[7] = 0;
473
0
  pnt[10] = 0;
474
0
  pnt[16] = 0;
475
0
  pnt[24] = 0;
476
0
  sprintf ((char *) tbuf, "%2s-%3s-%s %s",
477
0
     pnt + 8, pnt + 4, pnt + 20, pnt + 11);
478
#else
479
  struct
480
  {
481
    int Size;
482
    unsigned char *Ptr;
483
  } Descriptor;
484
  Descriptor.Size = 17;
485
  Descriptor.Ptr = tbuf;
486
  SYS$ASCTIM (0, &Descriptor, 0, 0);
487
#endif /* not VMS */
488
489
0
  vms_debug2 ((6, "vmstimestring:'%s'\n", tbuf));
490
491
0
  return tbuf;
492
0
}
493
494
/* Create module name from filename (ie, extract the basename and convert it
495
   in upper cases).  Works on both VMS and UNIX pathes.
496
   The result has to be free().  */
497
498
char *
499
vms_get_module_name (const char *filename, bool upcase)
500
0
{
501
0
  char *fname, *fptr;
502
0
  const char *fout;
503
504
  /* Strip VMS path.  */
505
0
  fout = strrchr (filename, ']');
506
0
  if (fout == NULL)
507
0
    fout = strchr (filename, ':');
508
0
  if (fout != NULL)
509
0
    fout++;
510
0
  else
511
0
    fout = filename;
512
513
  /* Strip UNIX path.  */
514
0
  fptr = strrchr (fout, '/');
515
0
  if (fptr != NULL)
516
0
    fout = fptr + 1;
517
518
0
  fname = strdup (fout);
519
520
  /* Strip suffix.  */
521
0
  fptr = strrchr (fname, '.');
522
0
  if (fptr != 0)
523
0
    *fptr = 0;
524
525
  /* Convert to upper case and truncate at 31 characters.
526
     (VMS object file format restricts module name length to 31).  */
527
0
  fptr = fname;
528
0
  for (fptr = fname; *fptr != 0; fptr++)
529
0
    {
530
0
      if (*fptr == ';' || (fptr - fname) >= 31)
531
0
  {
532
0
    *fptr = 0;
533
0
    break;
534
0
  }
535
0
      if (upcase)
536
0
  *fptr = TOUPPER (*fptr);
537
0
    }
538
0
  return fname;
539
0
}
540
541
/* Compared to usual UNIX time_t, VMS time has less limits:
542
   -  64 bit (63 bits in fact as the MSB must be 0)
543
   -  100ns granularity
544
   -  epoch is Nov 17, 1858.
545
   Here has the constants and the routines used to convert VMS from/to UNIX time.
546
   The conversion routines don't assume 64 bits arithmetic.
547
548
   Here we assume that the definition of time_t is the UNIX one, ie integer
549
   type, expressing seconds since the epoch.  */
550
551
/* UNIX time granularity for VMS, ie 1s / 100ns.  */
552
1.49k
#define VMS_TIME_FACTOR 10000000
553
554
/* Number of seconds since VMS epoch of the UNIX epoch.  */
555
87
#define VMS_TIME_OFFSET 3506716800U
556
557
/* Convert a VMS time to a unix time.  */
558
559
time_t
560
vms_time_to_time_t (unsigned int hi, unsigned int lo)
561
149
{
562
149
  unsigned int tmp;
563
149
  unsigned int rlo;
564
149
  int i;
565
149
  time_t res;
566
567
  /* First convert to seconds.  */
568
149
  tmp = hi % VMS_TIME_FACTOR;
569
149
  hi = hi / VMS_TIME_FACTOR;
570
149
  rlo = 0;
571
745
  for (i = 0; i < 4; i++)
572
596
    {
573
596
      tmp = (tmp << 8) | (lo >> 24);
574
596
      lo <<= 8;
575
576
596
      rlo = (rlo << 8) | (tmp / VMS_TIME_FACTOR);
577
596
      tmp %= VMS_TIME_FACTOR;
578
596
    }
579
149
  lo = rlo;
580
581
  /* Return 0 in case of overflow.  */
582
149
  if (hi > 1
583
149
      || (hi == 1 && lo >= VMS_TIME_OFFSET))
584
83
    return 0;
585
586
  /* Return 0 in case of underflow.  */
587
66
  if (hi == 0 && lo < VMS_TIME_OFFSET)
588
58
    return 0;
589
590
8
  res = lo - VMS_TIME_OFFSET;
591
8
  if (res <= 0)
592
0
    return 0;
593
8
  return res;
594
8
}
595
596
/* Convert a time_t to a VMS time.  */
597
598
void
599
vms_time_t_to_vms_time (time_t ut, unsigned int *hi, unsigned int *lo)
600
6
{
601
6
  unsigned int val[4];
602
6
  unsigned int tmp[4];
603
6
  unsigned int carry;
604
6
  int i;
605
606
  /* Put into val.  */
607
6
  val[0] = ut & 0xffff;
608
6
  val[1] = (ut >> 16) & 0xffff;
609
6
  val[2] = sizeof (ut) > 4 ? (ut >> 32) & 0xffff : 0;
610
6
  val[3] = sizeof (ut) > 4 ? (ut >> 48) & 0xffff : 0;
611
612
  /* Add offset.  */
613
6
  tmp[0] = VMS_TIME_OFFSET & 0xffff;
614
6
  tmp[1] = VMS_TIME_OFFSET >> 16;
615
6
  tmp[2] = 0;
616
6
  tmp[3] = 0;
617
6
  carry = 0;
618
30
  for (i = 0; i < 4; i++)
619
24
    {
620
24
      carry += tmp[i] + val[i];
621
24
      val[i] = carry & 0xffff;
622
24
      carry = carry >> 16;
623
24
    }
624
625
  /* Multiply by factor, well first by 10000 and then by 1000.  */
626
6
  carry = 0;
627
30
  for (i = 0; i < 4; i++)
628
24
    {
629
24
      carry += val[i] * 10000;
630
24
      val[i] = carry & 0xffff;
631
24
      carry = carry >> 16;
632
24
    }
633
6
  carry = 0;
634
30
  for (i = 0; i < 4; i++)
635
24
    {
636
24
      carry += val[i] * 1000;
637
24
      val[i] = carry & 0xffff;
638
24
      carry = carry >> 16;
639
24
    }
640
641
  /* Write the result.  */
642
6
  *lo = val[0] | (val[1] << 16);
643
6
  *hi = val[2] | (val[3] << 16);
644
6
}
645
646
/* Convert a raw (stored in a buffer) VMS time to a unix time.  */
647
648
time_t
649
vms_rawtime_to_time_t (unsigned char *buf)
650
149
{
651
149
  unsigned int hi = bfd_getl32 (buf + 4);
652
149
  unsigned int lo = bfd_getl32 (buf + 0);
653
654
149
  return vms_time_to_time_t (hi, lo);
655
149
}
656
657
void
658
vms_get_time (unsigned int *hi, unsigned int *lo)
659
6
{
660
#ifdef VMS
661
  struct _generic_64 t;
662
663
  sys$gettim (&t);
664
  *lo = t.gen64$q_quadword;
665
  *hi = t.gen64$q_quadword >> 32;
666
#else
667
6
  time_t t;
668
669
6
  time (&t);
670
6
  vms_time_t_to_vms_time (t, hi, lo);
671
6
#endif
672
6
}
673
674
/* Get the current time into a raw buffer BUF.  */
675
676
void
677
vms_raw_get_time (unsigned char *buf)
678
5
{
679
5
  unsigned int hi, lo;
680
681
5
  vms_get_time (&hi, &lo);
682
5
  bfd_putl32 (lo, buf + 0);
683
5
  bfd_putl32 (hi, buf + 4);
684
5
}