1/* codeview.c - CodeView debug support
2   Copyright (C) 2022-2024 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
35struct line
36{
37  struct line *next;
38  unsigned int lineno;
39  addressT frag_offset;
40};
41
42struct 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
50struct 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
60struct 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
69static struct line_block *blocks_head = NULL, *blocks_tail = NULL;
70static struct source_file *files_head = NULL, *files_tail = NULL;
71static unsigned int num_source_files = 0;
72
73/* Return the size of the current fragment (taken from dwarf2dbg.c).  */
74static offsetT
75get_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.  */
90static void
91emit_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.  */
103static void
104emit_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.  */
116static void
117write_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.  */
161static void
162write_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.  */
196static void
197write_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.  */
286static uint16_t
287target_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.  */
307static void
308write_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.  */
381void
382codeview_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.  */
403static unsigned int
404get_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.  */
458void
459codeview_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#else
537
538void
539codeview_finish (void)
540{
541}
542
543void
544codeview_generate_asm_lineno (void)
545{
546}
547
548#endif /* TE_PE && O_secrel */
549