[02/24] pdbout: Output details of local variables.

Message ID 20210320162652.23346-2-mark@harmstone.com
State New
Headers show
Series
  • [01/24] Add -gcodeview debugging option
Related show

Commit Message

Mark Harmstone March 20, 2021, 4:26 p.m.
---
 gcc/pdbout.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 gcc/pdbout.h |  89 +++++++++++
 2 files changed, 504 insertions(+), 2 deletions(-)

-- 
2.26.2

Patch

diff --git a/gcc/pdbout.c b/gcc/pdbout.c
index e8f39bb64ea..feaab37cc37 100644
--- a/gcc/pdbout.c
+++ b/gcc/pdbout.c
@@ -25,13 +25,37 @@ 
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "varasm.h"
 #include "tree.h"
 #include "debug.h"
 #include "pdbout.h"
+#include "output.h"
+#include "target.h"
+
+static void pdbout_finish (const char *filename);
+static void pdbout_late_global_decl (tree var);
+
+static struct pdb_type *find_type (tree t);
+
+static struct pdb_global_var *global_vars = NULL;
+static struct pdb_type *types = NULL, *last_type = NULL;
+static hash_table <pdb_type_tree_hasher> tree_hash_table (31);
+static struct pdb_type *byte_type, *signed_byte_type, *wchar_type,
+  *char16_type, *uint16_type, *int16_type, *char32_type, *uint32_type,
+  *int32_type, *uint64_type, *int64_type, *uint128_type, *int128_type,
+  *long_type, *ulong_type, *hresult_type;
+static struct pdb_type *float16_type, *float32_type, *float48_type,
+  *float64_type, *float80_type, *float128_type;
+static struct pdb_type *bool8_type, *bool16_type, *bool32_type, *bool64_type,
+  *bool128_type;
+static struct pdb_type *complex16_type, *complex32_type, *complex48_type,
+  *complex64_type, *complex80_type, *complex128_type;
+static struct pdb_type *void_type, *nullptr_type;
+static bool builtins_initialized = false;
 
 const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_charstar,	/* init */
-  debug_nothing_charstar,	/* finish */
+  pdbout_finish,
   debug_nothing_charstar,	/* early_finish */
   debug_nothing_void,		/* assembly_start */
   debug_nothing_int_charstar,	/* define */
@@ -51,7 +75,7 @@  const struct gcc_debug_hooks pdb_debug_hooks = {
   debug_nothing_tree,		/* register_main_translation_unit */
   debug_nothing_tree,		/* function_decl */
   debug_nothing_tree,		/* early_global_decl */
-  debug_nothing_tree,		/* late_global_decl */
+  pdbout_late_global_decl,
   debug_nothing_tree_int,	/* type_decl */
   debug_nothing_tree_tree_tree_bool_bool,	/* imported_module_or_decl */
   debug_false_tree_charstarstar_uhwistar,	/* die_ref_for_decl */
@@ -68,3 +92,392 @@  const struct gcc_debug_hooks pdb_debug_hooks = {
   0,				/* start_end_main_source_file */
   TYPE_SYMTAB_IS_ADDRESS	/* tree_type_symtab_field */
 };
+
+/* Output DATASYM32 structure, describing a global variable: either
+ * one with file-level scope (S_LDATA32) or global scope (S_GDATA32). */
+static void
+pdbout_data32 (struct pdb_global_var *v)
+{
+  size_t name_len = strlen (v->name);
+  uint16_t len;
+
+  // Outputs DATASYM32 struct
+
+  len = 15 + name_len;
+
+  if (len % 4 != 0)
+    len += 4 - (len % 4);
+
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+	   (uint16_t) (len - sizeof (uint16_t)));	// reclen
+  fprintf (asm_out_file, "\t.short\t0x%x\n",
+	   v->public_flag ? S_GDATA32 : S_LDATA32);
+  fprintf (asm_out_file, "\t.short\t0x%x\n", v->type ? v->type->id : 0);
+  fprintf (asm_out_file, "\t.short\t0\n");
+
+  fprintf (asm_out_file, "\t.secrel32\t");	// off
+  ASM_OUTPUT_LABELREF (asm_out_file, v->asm_name);
+  fprintf (asm_out_file, "\n");
+  fprintf (asm_out_file, "\t.secidx\t");	// section
+  ASM_OUTPUT_LABELREF (asm_out_file, v->asm_name);
+  fprintf (asm_out_file, "\n");
+
+  ASM_OUTPUT_ASCII (asm_out_file, v->name, name_len + 1);
+
+  fprintf (asm_out_file, "\t.balign\t4\n");
+}
+
+/* Output the .debug$S section, which has everything except the
+ * type definitions (global variables, functions, string table,
+ * file checksums, line numbers).
+ * The linker will extract this section from all the object
+ * files, remove any duplicate data, resolve all addresses,
+ * and output the resulting data into a PDB file. The section's
+ * marked as "ndr", so even if the linker doesn't understand it,
+ * the section won't make its way into final binary. */
+static void
+write_pdb_section (void)
+{
+  fprintf (asm_out_file, "\t.section\t.debug$S, \"ndr\"\n");
+  fprintf (asm_out_file, "\t.long\t0x%x\n", CV_SIGNATURE_C13);
+  fprintf (asm_out_file, "\t.long\t0x%x\n", DEBUG_S_SYMBOLS);
+  fprintf (asm_out_file, "\t.long\t[.Lsymend]-[.Lsymstart]\n");
+
+  fprintf (asm_out_file, ".Lsymstart:\n");
+
+  while (global_vars)
+    {
+      struct pdb_global_var *n;
+
+      pdbout_data32 (global_vars);
+
+      n = global_vars->next;
+
+      if (global_vars->name)
+	free (global_vars->name);
+
+      if (global_vars->asm_name)
+	free (global_vars->asm_name);
+
+      free (global_vars);
+
+      global_vars = n;
+    }
+
+  fprintf (asm_out_file, ".Lsymend:\n");
+}
+
+/* We've finished compilation - output the .debug$S section
+ * to the asm file. */
+static void
+pdbout_finish (const char *filename ATTRIBUTE_UNUSED)
+{
+  write_pdb_section ();
+}
+
+/* We've been passed a late global declaration, i.e. a global variable -
+ * allocate a pdb_global_var struct and add it to the list of globals. */
+static void
+pdbout_late_global_decl (tree var)
+{
+  struct pdb_global_var *v;
+
+  if (TREE_CODE (var) != VAR_DECL)
+    return;
+
+  if (!DECL_ASSEMBLER_NAME_RAW (var))
+    return;
+
+  // We take care of static variables in functions separately
+  if (DECL_CONTEXT (var) && TREE_CODE (DECL_CONTEXT (var)) == FUNCTION_DECL)
+    return;
+
+  if (!TREE_ASM_WRITTEN (var) || DECL_IGNORED_P (var))
+    return;
+
+  v = (struct pdb_global_var *) xmalloc (sizeof (struct pdb_global_var));
+
+  v->next = global_vars;
+  v->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (var)));
+  v->asm_name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME_RAW (var)));
+  v->public_flag = TREE_PUBLIC (var);
+  v->type = find_type (TREE_TYPE (var));
+
+  global_vars = v;
+}
+
+inline hashval_t
+pdb_type_tree_hasher::hash (pdb_type_tree_hasher::compare_type tree)
+{
+  return htab_hash_pointer (tree);
+}
+
+inline bool
+pdb_type_tree_hasher::equal (const value_type type, compare_type tree)
+{
+  return type->tree == tree;
+}
+
+static struct pdb_type *
+add_builtin_type (tree t, uint16_t id)
+{
+  struct pdb_type *type, **slot;
+
+  type = (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data));
+  type->cv_type = 0;
+  type->tree = t;
+  type->next = type->next2 = NULL;
+  type->id = id;
+
+  if (last_type)
+    last_type->next = type;
+  else
+    types = type;
+
+  last_type = type;
+
+  if (t)
+    {
+      slot =
+	tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t),
+					     INSERT);
+      *slot = type;
+    }
+
+  return type;
+}
+
+/* Initialize the builtin types, ones that we won't output: the integers,
+ * the floats, the bools, etc. Pointers to these are also counted as
+ * predefined types, but we take care of these in number_types. */
+static void
+add_builtin_types (void)
+{
+  add_builtin_type (char_type_node, CV_BUILTIN_TYPE_NARROW_CHARACTER);
+  add_builtin_type (signed_char_type_node, CV_BUILTIN_TYPE_SIGNED_CHARACTER);
+  add_builtin_type (unsigned_char_type_node,
+		    CV_BUILTIN_TYPE_UNSIGNED_CHARACTER);
+  add_builtin_type (short_integer_type_node, CV_BUILTIN_TYPE_INT16SHORT);
+  add_builtin_type (short_unsigned_type_node, CV_BUILTIN_TYPE_UINT16SHORT);
+  long_type =
+    add_builtin_type (long_integer_type_node, CV_BUILTIN_TYPE_INT32LONG);
+  ulong_type =
+    add_builtin_type (long_unsigned_type_node, CV_BUILTIN_TYPE_UINT32LONG);
+  add_builtin_type (long_long_integer_type_node, CV_BUILTIN_TYPE_INT64QUAD);
+  add_builtin_type (long_long_unsigned_type_node, CV_BUILTIN_TYPE_UINT64QUAD);
+
+  byte_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_BYTE);
+  signed_byte_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_SBYTE);
+  wchar_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_WIDE_CHARACTER);
+  char16_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_CHARACTER16);
+  uint16_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_UINT16);
+  int16_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_INT16);
+  char32_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_CHARACTER32);
+  uint32_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_UINT32);
+  int32_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_INT32);
+  uint64_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_UINT64);
+  int64_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_INT64);
+  uint128_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_UINT128);
+  int128_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_INT128);
+  hresult_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_HRESULT);
+
+  float16_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_FLOAT16);
+  float32_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_FLOAT32);
+  float48_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_FLOAT48);
+  float64_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_FLOAT64);
+  float80_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_FLOAT80);
+  float128_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_FLOAT128);
+
+  bool8_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_BOOLEAN8);
+  bool16_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_BOOLEAN16);
+  bool32_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_BOOLEAN32);
+  bool64_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_BOOLEAN64);
+  bool128_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_BOOLEAN128);
+
+  complex16_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_COMPLEX16);
+  complex32_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_COMPLEX32);
+  complex48_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_COMPLEX48);
+  complex64_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_COMPLEX64);
+  complex80_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_COMPLEX80);
+  complex128_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_COMPLEX128);
+
+  void_type = add_builtin_type (NULL, CV_BUILTIN_TYPE_VOID);
+  nullptr_type =
+    add_builtin_type (NULL, (CV_TM_NPTR << 8) | CV_BUILTIN_TYPE_VOID);
+
+  builtins_initialized = true;
+}
+
+/* Resolve a type t to a pdb_type struct. */
+static struct pdb_type *
+find_type (tree t)
+{
+  struct pdb_type *type;
+
+  if (!builtins_initialized)
+    add_builtin_types ();
+
+  if (!t)
+    return NULL;
+
+  // search through existing types
+
+  type = tree_hash_table.find_with_hash (t, pdb_type_tree_hasher::hash (t));
+
+  if (type)
+    return type;
+
+  switch (TREE_CODE (t))
+    {
+    case INTEGER_TYPE:
+      {
+	unsigned int size;
+
+	size = TREE_INT_CST_ELT (TYPE_SIZE (t), 0);
+
+	switch (size)
+	  {
+	  case 8:
+	    return TYPE_UNSIGNED (t) ? byte_type : signed_byte_type;
+
+	  case 16:
+	    if (TYPE_IDENTIFIER (t)
+		&& IDENTIFIER_POINTER (TYPE_IDENTIFIER (t))
+		&& !strcmp (IDENTIFIER_POINTER (TYPE_IDENTIFIER (t)),
+			    "wchar_t"))
+	      return wchar_type;
+	    else if (TYPE_IDENTIFIER (t)
+		     && IDENTIFIER_POINTER (TYPE_IDENTIFIER (t))
+		     && !strcmp (IDENTIFIER_POINTER (TYPE_IDENTIFIER (t)),
+				 "char16_t"))
+	      return char16_type;
+	    else
+	      return TYPE_UNSIGNED (t) ? uint16_type : int16_type;
+
+	  case 32:
+	    if (TYPE_IDENTIFIER (t)
+		&& IDENTIFIER_POINTER (TYPE_IDENTIFIER (t))
+		&& !strcmp (IDENTIFIER_POINTER (TYPE_IDENTIFIER (t)),
+			    "char32_t"))
+	      return char32_type;
+	    else
+	      return TYPE_UNSIGNED (t) ? uint32_type : int32_type;
+
+	  case 64:
+	    return TYPE_UNSIGNED (t) ? uint64_type : int64_type;
+
+	  case 128:
+	    return TYPE_UNSIGNED (t) ? uint128_type : int128_type;
+
+	  default:
+	    return NULL;
+	  }
+      }
+
+    case REAL_TYPE:
+      {
+	unsigned int size = TREE_INT_CST_ELT (TYPE_SIZE (t), 0);
+
+	switch (size)
+	  {
+	  case 16:
+	    return float16_type;
+
+	  case 32:
+	    return float32_type;
+
+	  case 48:
+	    return float48_type;
+
+	  case 64:
+	    return float64_type;
+
+	  case 80:
+	    return float80_type;
+
+	  case 128:
+	    return float128_type;
+
+	  default:
+	    return NULL;
+	  }
+      }
+
+    case BOOLEAN_TYPE:
+      {
+	unsigned int size = TREE_INT_CST_ELT (TYPE_SIZE (t), 0);
+
+	switch (size)
+	  {
+	  case 8:
+	    return bool8_type;
+
+	  case 16:
+	    return bool16_type;
+
+	  case 32:
+	    return bool32_type;
+
+	  case 64:
+	    return bool64_type;
+
+	  case 128:
+	    return bool128_type;
+
+	  default:
+	    return NULL;
+	  }
+      }
+
+    case COMPLEX_TYPE:
+      {
+	unsigned int size = TREE_INT_CST_ELT (TYPE_SIZE (t), 0);
+
+	switch (size)
+	  {
+	  case 16:
+	    return complex16_type;
+
+	  case 32:
+	    return complex32_type;
+
+	  case 48:
+	    return complex48_type;
+
+	  case 64:
+	    return complex64_type;
+
+	  case 80:
+	    return complex80_type;
+
+	  case 128:
+	    return complex128_type;
+
+	  default:
+	    return NULL;
+	  }
+      }
+
+    case VOID_TYPE:
+      return void_type;
+
+    case NULLPTR_TYPE:
+      return nullptr_type;
+
+    default:
+      break;
+    }
+
+  if (TYPE_MAIN_VARIANT (t) != t)
+    {
+      type =
+	tree_hash_table.find_with_hash (TYPE_MAIN_VARIANT (t),
+					pdb_type_tree_hasher::
+					hash (TYPE_MAIN_VARIANT (t)));
+
+      if (type)
+	return type;
+    }
+
+    return NULL;
+}
diff --git a/gcc/pdbout.h b/gcc/pdbout.h
index f957cd5eca1..e3430793ee7 100644
--- a/gcc/pdbout.h
+++ b/gcc/pdbout.h
@@ -20,4 +20,93 @@ 
 #ifndef GCC_PDBOUT_H
 #define GCC_PDBOUT_H 1
 
+#define S_LDATA32			0x110c
+#define S_GDATA32			0x110d
+
+/* Format version as of MSVC 7 */
+#define CV_SIGNATURE_C13	4
+
+#define DEBUG_S_SYMBOLS			0xf1
+
+struct pdb_global_var
+{
+  struct pdb_global_var *next;
+  char *name;
+  char *asm_name;
+  unsigned int public_flag;
+  struct pdb_type *type;
+};
+
+struct pdb_type
+{
+  struct pdb_type *next;
+  struct pdb_type *next2;
+  uint16_t id;
+  tree_node *tree;
+  uint16_t cv_type;
+  uint8_t data[1];
+};
+
+#define CV_BUILTIN_TYPE_VOID			0x0003
+#define CV_BUILTIN_TYPE_HRESULT			0x0008
+#define CV_BUILTIN_TYPE_SIGNED_CHARACTER	0x0010
+#define CV_BUILTIN_TYPE_INT16SHORT		0x0011
+#define CV_BUILTIN_TYPE_INT32LONG		0x0012
+#define CV_BUILTIN_TYPE_INT64QUAD		0x0013
+#define CV_BUILTIN_TYPE_UINT64QUAD		0x0023
+#define CV_BUILTIN_TYPE_UNSIGNED_CHARACTER	0x0020
+#define CV_BUILTIN_TYPE_UINT16SHORT		0x0021
+#define CV_BUILTIN_TYPE_UINT32LONG		0x0022
+#define CV_BUILTIN_TYPE_BOOLEAN8		0x0030
+#define CV_BUILTIN_TYPE_BOOLEAN16		0x0031
+#define CV_BUILTIN_TYPE_BOOLEAN32		0x0032
+#define CV_BUILTIN_TYPE_BOOLEAN64		0x0033
+#define CV_BUILTIN_TYPE_BOOLEAN128		0x0034
+#define CV_BUILTIN_TYPE_FLOAT16			0x0046
+#define CV_BUILTIN_TYPE_FLOAT32			0x0040
+#define CV_BUILTIN_TYPE_FLOAT48			0x0044
+#define CV_BUILTIN_TYPE_FLOAT64			0x0041
+#define CV_BUILTIN_TYPE_FLOAT80			0x0042
+#define CV_BUILTIN_TYPE_FLOAT128		0x0043
+#define CV_BUILTIN_TYPE_COMPLEX32		0x0050
+#define CV_BUILTIN_TYPE_COMPLEX64		0x0051
+#define CV_BUILTIN_TYPE_COMPLEX80		0x0052
+#define CV_BUILTIN_TYPE_COMPLEX128		0x0053
+#define CV_BUILTIN_TYPE_COMPLEX48		0x0054
+#define CV_BUILTIN_TYPE_COMPLEX16		0x0056
+#define CV_BUILTIN_TYPE_SBYTE			0x0068
+#define CV_BUILTIN_TYPE_BYTE			0x0069
+#define CV_BUILTIN_TYPE_NARROW_CHARACTER	0x0070
+#define CV_BUILTIN_TYPE_WIDE_CHARACTER		0x0071
+#define CV_BUILTIN_TYPE_INT16			0x0072
+#define CV_BUILTIN_TYPE_UINT16			0x0073
+#define CV_BUILTIN_TYPE_INT32			0x0074
+#define CV_BUILTIN_TYPE_UINT32			0x0075
+#define CV_BUILTIN_TYPE_INT64			0x0076
+#define CV_BUILTIN_TYPE_UINT64			0x0077
+#define CV_BUILTIN_TYPE_INT128			0x0078
+#define CV_BUILTIN_TYPE_UINT128			0x0079
+#define CV_BUILTIN_TYPE_CHARACTER16		0x007a
+#define CV_BUILTIN_TYPE_CHARACTER32		0x007b
+
+// from CV_prmode_e in cvdump
+#define CV_TM_NPTR			1
+#define CV_TM_NPTR32			4
+#define CV_TM_NPTR64			6
+
+struct pdb_type_tree_hasher : nofree_ptr_hash <struct pdb_type>
+{
+  typedef struct pdb_type *value_type;
+  typedef tree compare_type;
+
+  static inline hashval_t hash (compare_type);
+
+  static inline hashval_t hash (const value_type t)
+  {
+    return hash (t->tree);
+  }
+
+  static inline bool equal (const value_type, compare_type);
+};
+
 #endif