[v2,3/4] Output line numbers in CodeView section

Message ID 20231031002859.18892-4-mark@harmstone.com
State Unresolved
Headers
Series CodeView patches |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Mark Harmstone Oct. 31, 2023, 12:28 a.m. UTC
  Outputs the DEBUG_S_LINES block in the CodeView .debug$S section, which
maps between line numbers and addresses.

You'll need a fairly recent version of GAS for the .secidx directive to
be recognized.
---
 gcc/dwarf2codeview.cc | 303 ++++++++++++++++++++++++++++++++++++++++++
 gcc/dwarf2codeview.h  |   3 +
 gcc/dwarf2out.cc      |  15 +++
 gcc/opts.cc           |   2 +-
 4 files changed, 322 insertions(+), 1 deletion(-)
  

Patch

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index da8315310b5..9c69ebf8998 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -39,11 +39,15 @@  along with GCC; see the file COPYING3.  If not see
 
 #define CV_SIGNATURE_C13	4
 
+#define DEBUG_S_LINES		0xf2
 #define DEBUG_S_STRINGTABLE     0xf3
 #define DEBUG_S_FILECHKSMS      0xf4
 
 #define CHKSUM_TYPE_MD5		1
 
+#define LINE_LABEL	"Lcvline"
+#define END_FUNC_LABEL	"Lcvendfunc"
+
 #define HASH_SIZE 16
 
 struct codeview_string
@@ -91,11 +95,128 @@  struct codeview_source_file
   uint8_t hash[HASH_SIZE];
 };
 
+struct codeview_line
+{
+  codeview_line *next;
+  unsigned int line_no;
+  unsigned int label_num;
+};
+
+struct codeview_line_block
+{
+  codeview_line_block *next;
+  uint32_t file_id;
+  unsigned int num_lines;
+  codeview_line *lines, *last_line;
+};
+
+struct codeview_function
+{
+  codeview_function *next;
+  function *func;
+  unsigned int end_label;
+  codeview_line_block *blocks, *last_block;
+};
+
+static unsigned int line_label_num;
+static unsigned int func_label_num;
 static codeview_source_file *files, *last_file;
 static unsigned int num_files;
 static uint32_t string_offset = 1;
 static hash_table<string_hasher> *strings_htab;
 static codeview_string *strings, *last_string;
+static codeview_function *funcs, *last_func;
+static const char* last_filename;
+static uint32_t last_file_id;
+
+/* Record new line number against the current function.  */
+
+void
+codeview_source_line (unsigned int line_no, const char *filename)
+{
+  codeview_line *l;
+  uint32_t file_id = last_file_id;
+  unsigned int label_num = ++line_label_num;
+
+  targetm.asm_out.internal_label (asm_out_file, LINE_LABEL, label_num);
+
+  if (!last_func || last_func->func != cfun)
+    {
+      codeview_function *f = (codeview_function *)
+				xmalloc (sizeof (codeview_function));
+
+      f->next = NULL;
+      f->func = cfun;
+      f->end_label = 0;
+      f->blocks = f->last_block = NULL;
+
+      if (!funcs)
+	funcs = f;
+      else
+	last_func->next = f;
+
+      last_func = f;
+    }
+
+  if (filename != last_filename)
+    {
+      codeview_source_file *sf = files;
+
+      while (sf)
+	{
+	  if (!strcmp (sf->filename, filename))
+	    {
+	      /* 0x18 is the size of the checksum entry for each file.
+		 0x6 bytes for the header, plus 0x10 bytes for the hash,
+		 then padded to a multiple of 4.  */
+
+	      file_id = sf->file_num * 0x18;
+	      last_filename = filename;
+	      last_file_id = file_id;
+	      break;
+	    }
+
+	  sf = sf->next;
+	}
+    }
+
+  if (!last_func->last_block || last_func->last_block->file_id != file_id)
+    {
+      codeview_line_block *b;
+
+      b = (codeview_line_block *) xmalloc (sizeof (codeview_line_block));
+
+      b->next = NULL;
+      b->file_id = file_id;
+      b->num_lines = 0;
+      b->lines = b->last_line = NULL;
+
+      if (!last_func->blocks)
+	last_func->blocks = b;
+      else
+	last_func->last_block->next = b;
+
+      last_func->last_block = b;
+    }
+
+  if (last_func->last_block->last_line
+    && last_func->last_block->last_line->line_no == line_no)
+    return;
+
+  l = (codeview_line *) xmalloc (sizeof (codeview_line));
+
+  l->next = NULL;
+  l->line_no = line_no;
+  l->label_num = label_num;
+
+  if (!last_func->last_block->lines)
+    last_func->last_block->lines = l;
+  else
+    last_func->last_block->last_line->next = l;
+
+  last_func->last_block->last_line = l;
+  last_func->last_block->num_lines++;
+}
 
 /* Adds string to the string table, returning its offset.  If already present,
    this returns the offset of the existing string.  */
@@ -290,6 +411,187 @@  write_source_files (void)
   asm_fprintf (asm_out_file, "%LLcv_filechksms_end:\n");
 }
 
+/* Write out the line number information for each function into the
+   .debug$S section.  */
+
+static void
+write_line_numbers (void)
+{
+  unsigned int func_num = 0;
+
+  while (funcs)
+    {
+      codeview_function *next = funcs->next;
+      unsigned int first_label_num;
+
+      fputs (integer_asm_op (4, false), asm_out_file);
+      fprint_whex (asm_out_file, DEBUG_S_LINES);
+      putc ('\n', asm_out_file);
+
+      fputs (integer_asm_op (4, false), asm_out_file);
+      asm_fprintf (asm_out_file, "%LLcv_lines%u_end - %LLcv_lines%u_start\n",
+		   func_num, func_num);
+
+      asm_fprintf (asm_out_file, "%LLcv_lines%u_start:\n", func_num);
+
+      /* Output the header (struct cv_lines_header in binutils or
+	 CV_DebugSLinesHeader_t in Microsoft's cvinfo.h):
+
+	struct cv_lines_header
+	{
+	  uint32_t offset;
+	  uint16_t section;
+	  uint16_t flags;
+	  uint32_t length;
+	};
+      */
+
+      asm_fprintf (asm_out_file, "\t.secrel32\t%L" LINE_LABEL "%u\n",
+		   funcs->blocks->lines->label_num);
+      asm_fprintf (asm_out_file, "\t.secidx\t%L" LINE_LABEL "%u\n",
+		   funcs->blocks->lines->label_num);
+
+      /* flags */
+      fputs (integer_asm_op (2, false), asm_out_file);
+      fprint_whex (asm_out_file, 0);
+      putc ('\n', asm_out_file);
+
+      first_label_num = funcs->blocks->lines->label_num;
+
+      /* length */
+      fputs (integer_asm_op (4, false), asm_out_file);
+      asm_fprintf (asm_out_file,
+		   "%L" END_FUNC_LABEL "%u - %L" LINE_LABEL "%u\n",
+		   funcs->end_label, first_label_num);
+
+      while (funcs->blocks)
+	{
+	  codeview_line_block *next = funcs->blocks->next;
+
+	  /* Next comes the blocks, each block being a part of a function
+	     within the same source file (struct cv_lines_block in binutils or
+	     CV_DebugSLinesFileBlockHeader_t in Microsoft's cvinfo.h):
+
+	    struct cv_lines_block
+	    {
+	      uint32_t file_id;
+	      uint32_t num_lines;
+	      uint32_t length;
+	    };
+	  */
+
+	  /* file ID */
+	  fputs (integer_asm_op (4, false), asm_out_file);
+	  fprint_whex (asm_out_file, funcs->blocks->file_id);
+	  putc ('\n', asm_out_file);
+
+	  /* number of lines */
+	  fputs (integer_asm_op (4, false), asm_out_file);
+	  fprint_whex (asm_out_file, funcs->blocks->num_lines);
+	  putc ('\n', asm_out_file);
+
+	  /* length of code block: (num_lines * sizeof (struct cv_line)) +
+	     sizeof (struct cv_lines_block) */
+	  fputs (integer_asm_op (4, false), asm_out_file);
+	  fprint_whex (asm_out_file, (funcs->blocks->num_lines * 0x8) + 0xc);
+	  putc ('\n', asm_out_file);
+
+	  while (funcs->blocks->lines)
+	    {
+	      codeview_line *next = funcs->blocks->lines->next;
+
+	      /* Finally comes the line number information (struct cv_line in
+		 binutils or CV_Line_t in Microsoft's cvinfo.h):
+
+		struct cv_line
+		{
+		  uint32_t offset;
+		  uint32_t line_no;
+		};
+
+		Strictly speaking line_no is a bitfield: the bottom 24 bits
+		are the line number, and the top bit means "is a statement".
+	      */
+
+	      fputs (integer_asm_op (4, false), asm_out_file);
+	      asm_fprintf (asm_out_file,
+			   "%L" LINE_LABEL "%u - %L" LINE_LABEL "%u\n",
+			   funcs->blocks->lines->label_num, first_label_num);
+
+	      fputs (integer_asm_op (4, false), asm_out_file);
+	      fprint_whex (asm_out_file,
+			   0x80000000
+			   | (funcs->blocks->lines->line_no & 0xffffff));
+	      putc ('\n', asm_out_file);
+
+	      free (funcs->blocks->lines);
+
+	      funcs->blocks->lines = next;
+	    }
+
+	  free (funcs->blocks);
+
+	  funcs->blocks = next;
+	}
+
+      free (funcs);
+
+      asm_fprintf (asm_out_file, "%LLcv_lines%u_end:\n", func_num);
+      func_num++;
+
+      funcs = next;
+    }
+}
+
+/* Treat cold sections as separate functions, for the purposes of line
+   numbers.  */
+
+void
+codeview_switch_text_section (void)
+{
+  codeview_function *f;
+
+  if (last_func && last_func->end_label == 0)
+    {
+      unsigned int label_num = ++func_label_num;
+
+      targetm.asm_out.internal_label (asm_out_file, END_FUNC_LABEL,
+				      label_num);
+
+      last_func->end_label = label_num;
+    }
+
+  f = (codeview_function *) xmalloc (sizeof (codeview_function));
+
+  f->next = NULL;
+  f->func = cfun;
+  f->end_label = 0;
+  f->blocks = f->last_block = NULL;
+
+  if (!funcs)
+    funcs = f;
+  else
+    last_func->next = f;
+
+  last_func = f;
+}
+
+/* Mark the end of the current function.  */
+
+void
+codeview_end_epilogue (void)
+{
+  if (last_func && last_func->end_label == 0)
+    {
+      unsigned int label_num = ++func_label_num;
+
+      targetm.asm_out.internal_label (asm_out_file, END_FUNC_LABEL,
+				      label_num);
+
+      last_func->end_label = label_num;
+    }
+}
+
 /* Finish CodeView debug info emission.  */
 
 void
@@ -303,6 +605,7 @@  codeview_debug_finish (void)
 
   write_strings_table ();
   write_source_files ();
+  write_line_numbers ();
 }
 
 #endif
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index e2d732bb9b6..b6421b62d2e 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -26,6 +26,9 @@  along with GCC; see the file COPYING3.  If not see
 /* Debug Format Interface.  Used in dwarf2out.cc.  */
 
 extern void codeview_debug_finish (void);
+extern void codeview_source_line (unsigned int, const char *);
 extern void codeview_start_source_file (const char *);
+extern void codeview_switch_text_section ();
+extern void codeview_end_epilogue (void);
 
 #endif /* GCC_DWARF2CODEVIEW_H */
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index dfff6e88804..04ccb702180 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -1253,6 +1253,11 @@  dwarf2out_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
   if (dwarf2out_do_cfi_asm ())
     fprintf (asm_out_file, "\t.cfi_endproc\n");
 
+#ifdef CODEVIEW_DEBUGGING_INFO
+  if (codeview_debuginfo_p ())
+    codeview_end_epilogue ();
+#endif
+
   /* Output a label to mark the endpoint of the code generated for this
      function.  */
   ASM_GENERATE_INTERNAL_LABEL (label, FUNC_END_LABEL,
@@ -1306,6 +1311,11 @@  dwarf2out_switch_text_section (void)
     }
   have_multiple_function_sections = true;
 
+#ifdef CODEVIEW_DEBUGGING_INFO
+  if (codeview_debuginfo_p ())
+    codeview_switch_text_section ();
+#endif
+
   if (dwarf2out_do_cfi_asm ())
     fprintf (asm_out_file, "\t.cfi_endproc\n");
 
@@ -28605,6 +28615,11 @@  dwarf2out_source_line (unsigned int line, unsigned int column,
   dw_line_info_table *table;
   static var_loc_view lvugid;
 
+#ifdef CODEVIEW_DEBUGGING_INFO
+  if (codeview_debuginfo_p ())
+    codeview_source_line (line, filename);
+#endif
+
   /* 'line_info_table' information gathering is not needed when the debug
      info level is set to the lowest value.  Also, the current DWARF-based
      debug formats do not use this info.  */
diff --git a/gcc/opts.cc b/gcc/opts.cc
index f02101ceea3..6e91b1e0ff9 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -1364,7 +1364,7 @@  finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     opts->x_debug_nonbind_markers_p
       = (opts->x_optimize
 	 && opts->x_debug_info_level >= DINFO_LEVEL_NORMAL
-	 && dwarf_debuginfo_p (opts)
+	 && (dwarf_debuginfo_p (opts) || codeview_debuginfo_p ())
 	 && !(opts->x_flag_selective_scheduling
 	      || opts->x_flag_selective_scheduling2));