Coverage Report

Created: 2025-06-24 06:45

/src/binutils-gdb/gas/codeview.c
Line
Count
Source (jump to first uncovered line)
1
/* codeview.c - CodeView debug support
2
   Copyright (C) 2022-2025 Free Software Foundation, Inc.
3
4
   This file is part of GAS, the GNU Assembler.
5
6
   GAS is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 3, or (at your option)
9
   any later version.
10
11
   GAS is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with GAS; see the file COPYING.  If not, write to the Free
18
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
19
   02110-1301, USA.  */
20
21
#include "as.h"
22
#include "codeview.h"
23
#include "subsegs.h"
24
#include "filenames.h"
25
#include "md5.h"
26
27
#if defined (TE_PE) && defined (O_secrel)
28
29
#define NUM_MD5_BYTES         16
30
31
#define FILE_ENTRY_PADDING  2
32
#define FILE_ENTRY_LENGTH (sizeof (struct file_checksum) + NUM_MD5_BYTES \
33
         + FILE_ENTRY_PADDING)
34
35
struct line
36
{
37
  struct line *next;
38
  unsigned int lineno;
39
  addressT frag_offset;
40
};
41
42
struct line_file
43
{
44
  struct line_file *next;
45
  unsigned int fileno;
46
  struct line *lines_head, *lines_tail;
47
  unsigned int num_lines;
48
};
49
50
struct line_block
51
{
52
  struct line_block *next;
53
  segT seg;
54
  unsigned int subseg;
55
  fragS *frag;
56
  symbolS *sym;
57
  struct line_file *files_head, *files_tail;
58
};
59
60
struct source_file
61
{
62
  struct source_file *next;
63
  unsigned int num;
64
  char *filename;
65
  uint32_t string_pos;
66
  uint8_t md5[NUM_MD5_BYTES];
67
};
68
69
static struct line_block *blocks_head = NULL, *blocks_tail = NULL;
70
static struct source_file *files_head = NULL, *files_tail = NULL;
71
static unsigned int num_source_files = 0;
72
73
/* Return the size of the current fragment (taken from dwarf2dbg.c).  */
74
static offsetT
75
get_frag_fix (fragS *frag, segT seg)
76
{
77
  frchainS *fr;
78
79
  if (frag->fr_next)
80
    return frag->fr_fix;
81
82
  for (fr = seg_info (seg)->frchainP; fr; fr = fr->frch_next)
83
    if (fr->frch_last == frag)
84
      return (char *) obstack_next_free (&fr->frch_obstack) - frag->fr_literal;
85
86
  abort ();
87
}
88
89
/* Emit a .secrel32 relocation.  */
90
static void
91
emit_secrel32_reloc (symbolS *sym)
92
{
93
  expressionS exp;
94
95
  memset (&exp, 0, sizeof (exp));
96
  exp.X_op = O_secrel;
97
  exp.X_add_symbol = sym;
98
  exp.X_add_number = 0;
99
  emit_expr (&exp, sizeof (uint32_t));
100
}
101
102
/* Emit a .secidx relocation.  */
103
static void
104
emit_secidx_reloc (symbolS *sym)
105
{
106
  expressionS exp;
107
108
  memset (&exp, 0, sizeof (exp));
109
  exp.X_op = O_secidx;
110
  exp.X_add_symbol = sym;
111
  exp.X_add_number = 0;
112
  emit_expr (&exp, sizeof (uint16_t));
113
}
114
115
/* Write the DEBUG_S_STRINGTABLE subsection.  */
116
static void
117
write_string_table (void)
118
{
119
  uint32_t len;
120
  unsigned int padding;
121
  char *ptr, *start;
122
123
  len = 1;
124
125
  for (struct source_file *sf = files_head; sf; sf = sf->next)
126
    {
127
      len += strlen (sf->filename) + 1;
128
    }
129
130
  if (len % 4)
131
    padding = 4 - (len % 4);
132
  else
133
    padding = 0;
134
135
  ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len + padding);
136
137
  bfd_putl32 (DEBUG_S_STRINGTABLE, ptr);
138
  ptr += sizeof (uint32_t);
139
  bfd_putl32 (len, ptr);
140
  ptr += sizeof (uint32_t);
141
142
  start = ptr;
143
144
  *ptr = 0;
145
  ptr++;
146
147
  for (struct source_file *sf = files_head; sf; sf = sf->next)
148
    {
149
      size_t fn_len = strlen (sf->filename);
150
151
      sf->string_pos = ptr - start;
152
153
      memcpy(ptr, sf->filename, fn_len + 1);
154
      ptr += fn_len + 1;
155
    }
156
157
  memset (ptr, 0, padding);
158
}
159
160
/* Write the DEBUG_S_FILECHKSMS subsection.  */
161
static void
162
write_checksums (void)
163
{
164
  uint32_t len;
165
  char *ptr;
166
167
  len = FILE_ENTRY_LENGTH * num_source_files;
168
169
  ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len);
170
171
  bfd_putl32 (DEBUG_S_FILECHKSMS, ptr);
172
  ptr += sizeof (uint32_t);
173
  bfd_putl32 (len, ptr);
174
  ptr += sizeof (uint32_t);
175
176
  for (struct source_file *sf = files_head; sf; sf = sf->next)
177
    {
178
      struct file_checksum fc;
179
180
      fc.file_id = sf->string_pos;
181
      fc.checksum_length = NUM_MD5_BYTES;
182
      fc.checksum_type = CHKSUM_TYPE_MD5;
183
184
      memcpy (ptr, &fc, sizeof (struct file_checksum));
185
      ptr += sizeof (struct file_checksum);
186
187
      memcpy (ptr, sf->md5, NUM_MD5_BYTES);
188
      ptr += NUM_MD5_BYTES;
189
190
      memset (ptr, 0, FILE_ENTRY_PADDING);
191
      ptr += FILE_ENTRY_PADDING;
192
    }
193
}
194
195
/* Write the DEBUG_S_LINES subsection.  */
196
static void
197
write_lines_info (void)
198
{
199
  while (blocks_head)
200
    {
201
      struct line_block *lb;
202
      struct line_file *lf;
203
      uint32_t len;
204
      uint32_t off;
205
      char *ptr;
206
207
      lb = blocks_head;
208
209
      bfd_putl32 (DEBUG_S_LINES, frag_more (sizeof (uint32_t)));
210
211
      len = sizeof (struct cv_lines_header);
212
213
      for (lf = lb->files_head; lf; lf = lf->next)
214
  {
215
    len += sizeof (struct cv_lines_block);
216
    len += sizeof (struct cv_line) * lf->num_lines;
217
  }
218
219
      bfd_putl32 (len, frag_more (sizeof (uint32_t)));
220
221
      /* Write the header (struct cv_lines_header).  We can't use a struct
222
   for this as we're also emitting relocations.  */
223
224
      emit_secrel32_reloc (lb->sym);
225
      emit_secidx_reloc (lb->sym);
226
227
      ptr = frag_more (len - sizeof (uint32_t) - sizeof (uint16_t));
228
229
      /* Flags */
230
      bfd_putl16 (0, ptr);
231
      ptr += sizeof (uint16_t);
232
233
      off = lb->files_head->lines_head->frag_offset;
234
235
      /* Length of region */
236
      bfd_putl32 (get_frag_fix (lb->frag, lb->seg) - off, ptr);
237
      ptr += sizeof (uint32_t);
238
239
      while (lb->files_head)
240
  {
241
    struct cv_lines_block *block = (struct cv_lines_block *) ptr;
242
243
    lf = lb->files_head;
244
245
    bfd_putl32(lf->fileno * FILE_ENTRY_LENGTH, &block->file_id);
246
    bfd_putl32(lf->num_lines, &block->num_lines);
247
    bfd_putl32(sizeof (struct cv_lines_block)
248
         + (sizeof (struct cv_line) * lf->num_lines),
249
         &block->length);
250
251
    ptr += sizeof (struct cv_lines_block);
252
253
    while (lf->lines_head)
254
      {
255
        struct line *l;
256
        struct cv_line *l2 = (struct cv_line *) ptr;
257
258
        l = lf->lines_head;
259
260
        /* Only the bottom 24 bits of line_no actually encode the
261
     line number.  The top bit is a flag meaning "is
262
     a statement".  */
263
264
        bfd_putl32 (l->frag_offset - off, &l2->offset);
265
        bfd_putl32 (0x80000000 | (l->lineno & 0xffffff),
266
        &l2->line_no);
267
268
        lf->lines_head = l->next;
269
270
        free(l);
271
272
        ptr += sizeof (struct cv_line);
273
      }
274
275
    lb->files_head = lf->next;
276
    free (lf);
277
  }
278
279
      blocks_head = lb->next;
280
281
      free (lb);
282
    }
283
}
284
285
/* Return the CodeView constant for the selected architecture.  */
286
static uint16_t
287
target_processor (void)
288
{
289
  switch (stdoutput->arch_info->arch)
290
    {
291
    case bfd_arch_i386:
292
      if (stdoutput->arch_info->mach & bfd_mach_x86_64)
293
  return CV_CFL_X64;
294
      else
295
  return CV_CFL_80386;
296
297
    case bfd_arch_aarch64:
298
      return CV_CFL_ARM64;
299
300
    default:
301
      return 0;
302
    }
303
}
304
305
/* Write the CodeView symbols, describing the object name and
306
   assembler version.  */
307
static void
308
write_symbols_info (void)
309
{
310
  static const char assembler[] = "GNU AS " VERSION;
311
312
  char *path = lrealpath (out_file_name);
313
  char *path2 = remap_debug_filename (path);
314
  size_t path_len, padding;
315
  uint32_t len;
316
  struct OBJNAMESYM objname;
317
  struct COMPILESYM3 compile3;
318
  char *ptr;
319
320
  free (path);
321
  path = path2;
322
323
  path_len = strlen (path);
324
325
  len = sizeof (struct OBJNAMESYM) + path_len + 1;
326
  len += sizeof (struct COMPILESYM3) + sizeof (assembler);
327
328
  if (len % 4)
329
    padding = 4 - (len % 4);
330
  else
331
    padding = 0;
332
333
  len += padding;
334
335
  ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len);
336
337
  bfd_putl32 (DEBUG_S_SYMBOLS, ptr);
338
  ptr += sizeof (uint32_t);
339
  bfd_putl32 (len, ptr);
340
  ptr += sizeof (uint32_t);
341
342
  /* Write S_OBJNAME entry.  */
343
344
  bfd_putl16 (sizeof (struct OBJNAMESYM) - sizeof (uint16_t) + path_len + 1,
345
        &objname.length);
346
  bfd_putl16 (S_OBJNAME, &objname.type);
347
  bfd_putl32 (0, &objname.signature);
348
349
  memcpy (ptr, &objname, sizeof (struct OBJNAMESYM));
350
  ptr += sizeof (struct OBJNAMESYM);
351
  memcpy (ptr, path, path_len + 1);
352
  ptr += path_len + 1;
353
354
  free (path);
355
356
  /* Write S_COMPILE3 entry.  */
357
358
  bfd_putl16 (sizeof (struct COMPILESYM3) - sizeof (uint16_t)
359
        + sizeof (assembler) + padding, &compile3.length);
360
  bfd_putl16 (S_COMPILE3, &compile3.type);
361
  bfd_putl32 (CV_CFL_MASM, &compile3.flags);
362
  bfd_putl16 (target_processor (), &compile3.machine);
363
  bfd_putl16 (0, &compile3.frontend_major);
364
  bfd_putl16 (0, &compile3.frontend_minor);
365
  bfd_putl16 (0, &compile3.frontend_build);
366
  bfd_putl16 (0, &compile3.frontend_qfe);
367
  bfd_putl16 (0, &compile3.backend_major);
368
  bfd_putl16 (0, &compile3.backend_minor);
369
  bfd_putl16 (0, &compile3.backend_build);
370
  bfd_putl16 (0, &compile3.backend_qfe);
371
372
  memcpy (ptr, &compile3, sizeof (struct COMPILESYM3));
373
  ptr += sizeof (struct COMPILESYM3);
374
  memcpy (ptr, assembler, sizeof (assembler));
375
  ptr += sizeof (assembler);
376
377
  memset (ptr, 0, padding);
378
}
379
380
/* Processing of the file has finished, emit the .debug$S section.  */
381
void
382
codeview_finish (void)
383
{
384
  segT seg;
385
386
  if (!blocks_head)
387
    return;
388
389
  seg = subseg_new (".debug$S", 0);
390
391
  bfd_set_section_flags (seg, SEC_READONLY | SEC_NEVER_LOAD);
392
393
  bfd_putl32 (CV_SIGNATURE_C13, frag_more (sizeof (uint32_t)));
394
395
  write_string_table ();
396
  write_checksums ();
397
  write_lines_info ();
398
  write_symbols_info ();
399
}
400
401
/* Assign a new index number for the given file, or return the existing
402
   one if already assigned.  */
403
static unsigned int
404
get_fileno (const char *file)
405
{
406
  struct source_file *sf;
407
  char *path = lrealpath (file);
408
  char *path2 = remap_debug_filename (path);
409
  size_t path_len;
410
  FILE *f;
411
412
  free (path);
413
  path = path2;
414
415
  path_len = strlen (path);
416
417
  for (sf = files_head; sf; sf = sf->next)
418
    {
419
      if (path_len == strlen (sf->filename)
420
    && !filename_ncmp (sf->filename, path, path_len))
421
  {
422
    free (path);
423
    return sf->num;
424
  }
425
    }
426
427
  sf = xmalloc (sizeof (struct source_file));
428
429
  sf->next = NULL;
430
  sf->num = num_source_files;
431
  sf->filename = path;
432
433
  f = fopen (file, "r");
434
  if (!f)
435
    as_fatal (_("could not open %s for reading"), file);
436
437
  if (md5_stream (f, sf->md5))
438
    {
439
      fclose(f);
440
      as_fatal (_("md5_stream failed"));
441
    }
442
443
  fclose(f);
444
445
  if (!files_head)
446
    files_head = sf;
447
  else
448
    files_tail->next = sf;
449
450
  files_tail = sf;
451
452
  num_source_files++;
453
454
  return num_source_files - 1;
455
}
456
457
/* Called for each new line in asm file.  */
458
void
459
codeview_generate_asm_lineno (void)
460
{
461
  const char *file;
462
  unsigned int filenr;
463
  unsigned int lineno;
464
  struct line *l;
465
  symbolS *sym = NULL;
466
  struct line_block *lb;
467
  struct line_file *lf;
468
469
  file = as_where (&lineno);
470
471
  filenr = get_fileno (file);
472
473
  if (!blocks_tail || blocks_tail->frag != frag_now)
474
    {
475
      static int label_num = 0;
476
      char name[32];
477
478
      sprintf (name, ".Loc.%u", label_num);
479
      label_num++;
480
      sym = symbol_new (name, now_seg, frag_now, frag_now_fix ());
481
482
      lb = xmalloc (sizeof (struct line_block));
483
      lb->next = NULL;
484
      lb->seg = now_seg;
485
      lb->subseg = now_subseg;
486
      lb->frag = frag_now;
487
      lb->sym = sym;
488
      lb->files_head = lb->files_tail = NULL;
489
490
      if (!blocks_head)
491
  blocks_head = lb;
492
      else
493
  blocks_tail->next = lb;
494
495
      blocks_tail = lb;
496
    }
497
  else
498
    {
499
      lb = blocks_tail;
500
    }
501
502
  if (!lb->files_tail || lb->files_tail->fileno != filenr)
503
    {
504
      lf = xmalloc (sizeof (struct line_file));
505
      lf->next = NULL;
506
      lf->fileno = filenr;
507
      lf->lines_head = lf->lines_tail = NULL;
508
      lf->num_lines = 0;
509
510
      if (!lb->files_head)
511
  lb->files_head = lf;
512
      else
513
  lb->files_tail->next = lf;
514
515
      lb->files_tail = lf;
516
    }
517
  else
518
    {
519
      lf = lb->files_tail;
520
    }
521
522
  l = xmalloc (sizeof (struct line));
523
  l->next = NULL;
524
  l->lineno = lineno;
525
  l->frag_offset = frag_now_fix ();
526
527
  if (!lf->lines_head)
528
    lf->lines_head = l;
529
  else
530
    lf->lines_tail->next = l;
531
532
  lf->lines_tail = l;
533
  lf->num_lines++;
534
}
535
536
/* Output a compressed CodeView integer.  The return value is the number of
537
   bytes used.  */
538
539
unsigned int
540
output_cv_comp (char *p, offsetT value, int sign)
541
{
542
  char *orig = p;
543
544
  if (sign)
545
    {
546
      if (value < -0xfffffff || value > 0xfffffff)
547
  {
548
    as_bad (_("value cannot be expressed as a .cv_scomp"));
549
    return 0;
550
  }
551
    }
552
  else
553
    {
554
      if (value < 0 || value > 0x1fffffff)
555
  {
556
    as_bad (_("value cannot be expressed as a .cv_ucomp"));
557
    return 0;
558
  }
559
    }
560
561
  if (sign)
562
    {
563
      if (value >= 0)
564
  value <<= 1;
565
      else
566
  value = (-value << 1) | 1;
567
    }
568
569
  if (value <= 0x7f)
570
    {
571
      *p++ = value;
572
    }
573
  else if (value <= 0x3fff)
574
    {
575
      *p++ = 0x80 | (value >> 8);
576
      *p++ = value & 0xff;
577
    }
578
  else
579
    {
580
      *p++ = 0xc0 | (value >> 24);
581
      *p++ = (value >> 16) & 0xff;
582
      *p++ = (value >> 8) & 0xff;
583
      *p++ = value & 0xff;
584
    }
585
586
  return p - orig;
587
}
588
589
/* Return the size needed to output a compressed CodeView integer.  */
590
591
unsigned int
592
sizeof_cv_comp (offsetT value, int sign)
593
{
594
  if (sign)
595
    {
596
      if (value < -0xfffffff || value > 0xfffffff)
597
  return 0;
598
599
      if (value >= 0)
600
  value <<= 1;
601
      else
602
  value = (-value << 1) | 1;
603
    }
604
  else
605
    {
606
      if (value > 0x1fffffff)
607
  return 0;
608
    }
609
610
  if (value <= 0x7f)
611
    return 1;
612
  else if (value <= 0x3fff)
613
    return 2;
614
  else if (value <= 0x1fffffff)
615
    return 4;
616
  else
617
    return 0;
618
}
619
620
#else
621
622
void
623
codeview_finish (void)
624
28
{
625
28
}
626
627
void
628
codeview_generate_asm_lineno (void)
629
0
{
630
0
}
631
632
#endif /* TE_PE && O_secrel */