Home | History | Annotate | Line # | Download | only in gas
codeview.c revision 1.1.1.2
      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 {
    625 }
    626 
    627 void
    628 codeview_generate_asm_lineno (void)
    629 {
    630 }
    631 
    632 #endif /* TE_PE && O_secrel */
    633