diff mbox series

[08/24] pdbout: Output function prototypes.

Message ID 20210320162652.23346-8-mark@harmstone.com
State New
Headers show
Series [01/24] Add -gcodeview debugging option | expand

Commit Message

Mark Harmstone March 20, 2021, 4:26 p.m. UTC
---
 gcc/pdbout.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 gcc/pdbout.h |  23 ++++
 2 files changed, 359 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index 040ac6fe8e4..64f7c1d71bc 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -32,6 +32,7 @@ 
 #include "function.h"
 #include "output.h"
 #include "target.h"
+#include "config/i386/i386-protos.h"
 #include "md5.h"
 #include "rtl.h"
 #include "insn-config.h"
@@ -43,6 +44,8 @@ 
 #define FUNC_BEGIN_LABEL	".Lstartfunc"
 #define FUNC_END_LABEL		".Lendfunc"
 
+#define FIRST_TYPE_NUM		0x1000
+
 static void pdbout_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
 				   unsigned int column ATTRIBUTE_UNUSED,
 				   const char *file ATTRIBUTE_UNUSED);
@@ -72,6 +75,8 @@  static struct pdb_func *funcs = NULL, *cur_func = NULL;
 static struct pdb_block *cur_block = NULL;
 static struct pdb_global_var *global_vars = NULL;
 static struct pdb_type *types = NULL, *last_type = NULL;
+static struct pdb_type *arglist_types = NULL;
+static struct pdb_type *proc_types = NULL;
 static struct pdb_source_file *source_files = NULL, *last_source_file = NULL;
 static uint32_t source_file_string_offset = 1;
 static unsigned int num_line_number_entries = 0;
@@ -791,12 +796,142 @@  write_pdb_section (void)
     }
 }
 
-/* We've finished compilation - output the .debug$S section
+/* Free a pdb_type that we've allocated. */
+static void
+free_type (struct pdb_type *t)
+{
+  free (t);
+}
+
+/* Output a lfArgList structure, describing the arguments that a
+ * procedure expects. */
+static void
+write_arglist (struct pdb_arglist *arglist)
+{
+  unsigned int len = 8 + (4 * arglist->count);
+
+  if (arglist->count == 0)	// zero-length arglist has dummy entry
+    len += 4;
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_ARGLIST);
+  fprintf (asm_out_file, "\t.long\t0x%x\n",
+	   arglist->count == 0 ? 1 : arglist->count);
+
+  for (unsigned int i = 0; i < arglist->count; i++)
+    {
+      fprintf (asm_out_file, "\t.short\t0x%x\n",
+	       arglist->args[i] ? arglist->args[i]->id : 0);
+      fprintf (asm_out_file, "\t.short\t0\n");	// padding
+    }
+
+  if (arglist->count == 0)
+    {
+      fprintf (asm_out_file, "\t.short\t0\n");	// empty type
+      fprintf (asm_out_file, "\t.short\t0\n");	// padding
+    }
+}
+
+/* Output a lfProc structure, which describes the prototype of a
+ * procedure. See also pdbout_proc32, which outputs the details of
+ * a specific procedure. */
+static void
+write_procedure (struct pdb_proc *proc)
+{
+  fprintf (asm_out_file, "\t.short\t0xe\n");
+  fprintf (asm_out_file, "\t.short\t0x%x\n", LF_PROCEDURE);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+	   proc->return_type ? proc->return_type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");	// padding
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", proc->calling_convention);
+  fprintf (asm_out_file, "\t.byte\t0x%x\n", proc->attributes);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", proc->num_args);
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+	   proc->arg_list ? proc->arg_list->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");	// padding
+}
+
+/* Given a pdb_type, output its definition. */
+static void
+write_type (struct pdb_type *t)
+{
+  switch (t->cv_type)
+    {
+    case LF_ARGLIST:
+      write_arglist ((struct pdb_arglist *) t->data);
+      break;
+
+    case LF_PROCEDURE:
+      write_procedure ((struct pdb_proc *) t->data);
+      break;
+    }
+}
+
+/* Output the .debug$T section, which contains all the types used. */
+static void
+write_pdb_type_section (void)
+{
+  struct pdb_type *n;
+
+  fprintf (asm_out_file, "\t.section\t.debug$T, \"ndr\"\n");
+  fprintf (asm_out_file, "\t.long\t0x%x\n", CV_SIGNATURE_C13);
+
+  n = types;
+  while (n)
+    {
+      write_type (n);
+
+      n = n->next;
+    }
+
+  while (types)
+    {
+      n = types->next;
+
+      free_type (types);
+
+      types = n;
+    }
+}
+
+/* Loop through our types and assign them sequential numbers. */
+static void
+number_types (void)
+{
+  struct pdb_type *t;
+  uint16_t type_num = FIRST_TYPE_NUM;
+
+  t = types;
+  while (t)
+    {
+      if (t->id != 0)
+	{
+	  t = t->next;
+	  continue;
+	}
+
+      t->id = type_num;
+      type_num++;
+
+      if (type_num == 0)	// overflow
+	{
+	  fprintf (stderr, "too many CodeView types\n");
+	  xexit (1);
+	}
+
+      t = t->next;
+    }
+}
+
+/* We've finished compilation - output the .debug$S and .debug$T sections
  * to the asm file. */
 static void
 pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
 {
+  number_types ();
+
   write_pdb_section ();
+  write_pdb_type_section ();
 }
 
 /* For a tree t, construct the name. */
@@ -877,6 +1012,197 @@  pdbout_late_global_decl (tree var)
   global_vars = v;
 }
 
+/* Add an argument list type. */
+static pdb_type *
+add_arglist_type (struct pdb_type *t)
+{
+  struct pdb_type *t2 = arglist_types;
+  struct pdb_type *last_entry = NULL;
+
+  // check for dupes
+
+  while (t2)
+    {
+      struct pdb_arglist *arglist1 = (struct pdb_arglist *) t->data;
+      struct pdb_arglist *arglist2 = (struct pdb_arglist *) t2->data;
+
+      if (arglist1->count == arglist2->count)
+	{
+	  bool same = true;
+
+	  for (unsigned int i = 0; i < arglist1->count; i++)
+	    {
+	      if (arglist1->args[i] != arglist2->args[i])
+		{
+		  same = false;
+		  break;
+		}
+	    }
+
+	  if (same)
+	    {
+	      free (t);
+
+	      return t2;
+	    }
+	}
+
+      last_entry = t2;
+      t2 = t2->next2;
+    }
+
+  // add new
+
+  t->next = NULL;
+  t->next2 = NULL;
+  t->id = 0;
+
+  if (last_type)
+    last_type->next = t;
+  else
+    types = t;
+
+  last_type = t;
+
+  if (last_entry)
+    last_entry->next2 = t;
+  else
+    arglist_types = t;
+
+  return t;
+}
+
+/* Given a function type t, allocate a new pdb_type and add it to the
+ * type list. */
+static struct pdb_type *
+find_type_function (tree t)
+{
+  struct pdb_type *arglisttype, *proctype, *last_entry = NULL;
+  struct pdb_arglist *arglist;
+  struct pdb_proc *proc;
+  tree arg;
+  unsigned int num_args = 0;
+  struct pdb_type **argptr;
+  struct pdb_type *return_type;
+  uint8_t calling_convention;
+  struct pdb_type **slot;
+
+  // create arglist
+
+  arg = TYPE_ARG_TYPES (t);
+  while (arg)
+    {
+      if (TREE_CODE (TREE_VALUE (arg)) != VOID_TYPE)
+	num_args++;
+
+      arg = TREE_CHAIN (arg);
+    }
+
+  arglisttype =
+    (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+				 offsetof (struct pdb_arglist, args) +
+				 (num_args * sizeof (struct pdb_type *)));
+  arglisttype->cv_type = LF_ARGLIST;
+  arglisttype->tree = NULL;
+
+  arglist = (struct pdb_arglist *) arglisttype->data;
+  arglist->count = num_args;
+
+  argptr = arglist->args;
+  arg = TYPE_ARG_TYPES (t);
+  while (arg)
+    {
+      if (TREE_CODE (TREE_VALUE (arg)) != VOID_TYPE)
+	{
+	  *argptr = find_type (TREE_VALUE (arg));
+	  argptr++;
+	}
+
+      arg = TREE_CHAIN (arg);
+    }
+
+  arglisttype = add_arglist_type (arglisttype);
+
+  // create procedure
+
+  return_type = find_type (TREE_TYPE (t));
+
+  if (TARGET_64BIT)
+    calling_convention = CV_CALL_NEAR_C;
+  else
+    {
+      switch (ix86_get_callcvt (t))
+	{
+	case IX86_CALLCVT_CDECL:
+	  calling_convention = CV_CALL_NEAR_C;
+	  break;
+
+	case IX86_CALLCVT_STDCALL:
+	  calling_convention = CV_CALL_NEAR_STD;
+	  break;
+
+	case IX86_CALLCVT_FASTCALL:
+	  calling_convention = CV_CALL_NEAR_FAST;
+	  break;
+
+	case IX86_CALLCVT_THISCALL:
+	  calling_convention = CV_CALL_THISCALL;
+	  break;
+
+	default:
+	  calling_convention = CV_CALL_NEAR_C;
+	}
+    }
+
+  proctype = proc_types;
+  while (proctype)
+    {
+      proc = (struct pdb_proc *) proctype->data;
+
+      if (proc->return_type == return_type
+	  && proc->calling_convention == calling_convention
+	  && proc->num_args == num_args && proc->arg_list == arglisttype)
+	return proctype;
+
+      last_entry = proctype;
+      proctype = proctype->next2;
+    }
+
+  proctype =
+    (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+				 sizeof (struct pdb_proc));
+  proctype->cv_type = LF_PROCEDURE;
+  proctype->tree = t;
+  proctype->next = proctype->next2 = NULL;
+  proctype->id = 0;
+
+  proc = (struct pdb_proc *) proctype->data;
+
+  proc->return_type = return_type;
+  proc->attributes = 0;
+  proc->num_args = num_args;
+  proc->arg_list = arglisttype;
+  proc->calling_convention = calling_convention;
+
+  if (last_entry)
+    last_entry->next2 = proctype;
+  else
+    proc_types = proctype;
+
+  if (last_type)
+    last_type->next = proctype;
+  else
+    types = proctype;
+
+  last_type = proctype;
+
+  slot =
+    tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t), INSERT);
+  *slot = proctype;
+
+  return proctype;
+}
+
 inline hashval_t
 pdb_type_tree_hasher::hash (pdb_type_tree_hasher::compare_type tree)
 {
@@ -1150,7 +1476,15 @@  find_type (tree t)
 	return type;
     }
 
-    return NULL;
+  switch (TREE_CODE (t))
+    {
+    case FUNCTION_TYPE:
+    case METHOD_TYPE:
+      return find_type_function (t);
+
+    default:
+      return NULL;
+    }
 }
 
 #ifndef _WIN32
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index 8a6fef3c256..b9e56a66069 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -21,6 +21,7 @@ 
 #define GCC_PDBOUT_H 1
 
 #define S_END				0x0006
+#define LF_PROCEDURE			0x1008
 #define S_BLOCK32			0x1103
 #define S_REGISTER			0x1106
 #define S_BPREL32			0x110b
@@ -32,6 +33,7 @@ 
 #define S_LOCAL				0x113e
 #define S_DEFRANGE_REGISTER		0x1141
 #define S_DEFRANGE_REGISTER_REL		0x1145
+#define LF_ARGLIST			0x1201
 
 /* Format version as of MSVC 7 */
 #define CV_SIGNATURE_C13	4
@@ -122,6 +124,21 @@  struct pdb_global_var
   struct pdb_type *type;
 };
 
+struct pdb_arglist
+{
+  unsigned int count;
+  struct pdb_type *args[1];
+};
+
+struct pdb_proc
+{
+  struct pdb_type *return_type;
+  uint8_t calling_convention;
+  uint8_t attributes;
+  uint16_t num_args;
+  struct pdb_type *arg_list;
+};
+
 struct pdb_type
 {
   struct pdb_type *next;
@@ -179,6 +196,12 @@  struct pdb_type
 #define CV_TM_NPTR32			4
 #define CV_TM_NPTR64			6
 
+// from CV_call_e in cvdump
+#define CV_CALL_NEAR_C		0x00
+#define CV_CALL_NEAR_FAST	0x04
+#define CV_CALL_NEAR_STD	0x07
+#define CV_CALL_THISCALL	0x0b
+
 struct pdb_type_tree_hasher : nofree_ptr_hash <struct pdb_type>
 {
   typedef struct pdb_type *value_type;