diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index 160e64b..ff3ce76 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -1102,9 +1102,8 @@
 write_ambiguous_var (struct parser_state *par_state,
 		     const struct block *block, char *name, int len)
 {
-  struct symbol *sym = XOBNEW (&temp_parse_space, struct symbol);
+  struct symbol *sym = new (&temp_parse_space) symbol ();
 
-  memset (sym, 0, sizeof (struct symbol));
   SYMBOL_DOMAIN (sym) = UNDEF_DOMAIN;
   SYMBOL_LINKAGE_NAME (sym) = obstack_strndup (&temp_parse_space, name, len);
   SYMBOL_LANGUAGE (sym) = language_ada;
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 954a610..24d1e0f 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -135,7 +135,7 @@
   struct pending *link;
 
   /* If this is an alias for another symbol, don't add it.  */
-  if (symbol->ginfo.name && symbol->ginfo.name[0] == '#')
+  if (symbol->name && symbol->name[0] == '#')
     return;
 
   /* We keep PENDINGSIZE symbols in each link of the list.  If we
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 0a7a033..bbfa442 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -14320,8 +14320,7 @@
 	{
 	  struct objfile *objfile = cu->per_cu->dwarf2_per_objfile->objfile;
 
-	  storage = OBSTACK_ZALLOC (&objfile->objfile_obstack,
-				    struct rust_vtable_symbol);
+	  storage = new (&objfile->objfile_obstack) rust_vtable_symbol ();
 	  initialize_objfile_symbol (storage);
 	  storage->concrete_type = containing_type;
 	  storage->subclass = SYMBOL_RUST_VTABLE;
@@ -21636,8 +21635,8 @@
       /* Fortran does not have mangling standard and the mangling does differ
 	 between gfortran, iFort etc.  */
       if (cu->language == language_fortran
-          && symbol_get_demangled_name (&(sym->ginfo)) == NULL)
-	symbol_set_demangled_name (&(sym->ginfo),
+          && symbol_get_demangled_name (sym) == NULL)
+	symbol_set_demangled_name (sym,
 				   dwarf2_full_name (name, die, cu),
 	                           NULL);
 
diff --git a/gdb/jit.c b/gdb/jit.c
index 08fd862..85a01ef 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -700,8 +700,8 @@
       SYMBOL_TYPE (block_name) = lookup_function_type (block_type);
       SYMBOL_BLOCK_VALUE (block_name) = new_block;
 
-      block_name->ginfo.name = obstack_strdup (&objfile->objfile_obstack,
-					       gdb_block_iter->name);
+      block_name->name = obstack_strdup (&objfile->objfile_obstack,
+					 gdb_block_iter->name);
 
       BLOCK_FUNCTION (new_block) = block_name;
 
diff --git a/gdb/language.c b/gdb/language.c
index 0e13c71..6ab0ca3 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -1048,10 +1048,10 @@
   gdb_assert (!TYPE_OBJFILE_OWNED (type));
 
   gdbarch = TYPE_OWNER (type).gdbarch;
-  symbol = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct symbol);
+  symbol = new (gdbarch_obstack (gdbarch)) struct symbol ();
 
-  symbol->ginfo.name = TYPE_NAME (type);
-  symbol->ginfo.language = lang;
+  symbol->name = TYPE_NAME (type);
+  symbol->language = lang;
   symbol->owner.arch = gdbarch;
   SYMBOL_OBJFILE_OWNED (symbol) = 0;
   SYMBOL_TYPE (symbol) = type;
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 2c934b9..0064800 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1759,7 +1759,7 @@
       return sym;
     }
 
-  fixup_section (&sym->ginfo, addr, objfile);
+  fixup_section (sym, addr, objfile);
 
   return sym;
 }
@@ -6208,23 +6208,13 @@
 
 
 
-/* Helper function to initialize the fields of an objfile-owned symbol.
-   It assumed that *SYM is already all zeroes.  */
-
-static void
-initialize_objfile_symbol_1 (struct symbol *sym)
-{
-  SYMBOL_OBJFILE_OWNED (sym) = 1;
-  SYMBOL_SECTION (sym) = -1;
-}
-
 /* Initialize the symbol SYM, and mark it as being owned by an objfile.  */
 
 void
 initialize_objfile_symbol (struct symbol *sym)
 {
-  memset (sym, 0, sizeof (*sym));
-  initialize_objfile_symbol_1 (sym);
+  SYMBOL_OBJFILE_OWNED (sym) = 1;
+  SYMBOL_SECTION (sym) = -1;
 }
 
 /* Allocate and initialize a new 'struct symbol' on OBJFILE's
@@ -6233,10 +6223,9 @@
 struct symbol *
 allocate_symbol (struct objfile *objfile)
 {
-  struct symbol *result;
+  struct symbol *result = new (&objfile->objfile_obstack) symbol ();
 
-  result = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct symbol);
-  initialize_objfile_symbol_1 (result);
+  initialize_objfile_symbol (result);
 
   return result;
 }
@@ -6249,8 +6238,8 @@
 {
   struct template_symbol *result;
 
-  result = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct template_symbol);
-  initialize_objfile_symbol_1 (result);
+  result = new (&objfile->objfile_obstack) template_symbol ();
+  initialize_objfile_symbol (result);
 
   return result;
 }
@@ -6309,7 +6298,7 @@
       if (minsym.minsym != nullptr)
 	return BMSYMBOL_VALUE_ADDRESS (minsym);
     }
-  return sym->ginfo.value.address;
+  return sym->value.address;
 }
 
 /* See symtab.h.  */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 111a0f9..95d5931 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -25,6 +25,7 @@
 #include <string>
 #include "gdbsupport/gdb_vecs.h"
 #include "gdbtypes.h"
+#include "gdb_obstack.h"
 #include "gdb_regex.h"
 #include "gdbsupport/enum-flags.h"
 #include "gdbsupport/function-view.h"
@@ -470,35 +471,29 @@
 
 extern CORE_ADDR get_symbol_address (const struct symbol *sym);
 
-/* Note that all the following SYMBOL_* macros are used with the
-   SYMBOL argument being either a partial symbol or
-   a full symbol.  Both types have a ginfo field.  In particular
-   the SYMBOL_SET_LANGUAGE, SYMBOL_DEMANGLED_NAME, etc.
-   macros cannot be entirely substituted by
-   functions, unless the callers are changed to pass in the ginfo
-   field only, instead of the SYMBOL parameter.  */
+/* Note that these macros only work with symbol, not partial_symbol.  */
 
-#define SYMBOL_VALUE(symbol)		(symbol)->ginfo.value.ivalue
+#define SYMBOL_VALUE(symbol)		(symbol)->value.ivalue
 #define SYMBOL_VALUE_ADDRESS(symbol)			      \
   (((symbol)->maybe_copied) ? get_symbol_address (symbol)     \
-   : ((symbol)->ginfo.value.address))
+   : ((symbol)->value.address))
 #define SET_SYMBOL_VALUE_ADDRESS(symbol, new_value)	\
-  ((symbol)->ginfo.value.address = (new_value))
-#define SYMBOL_VALUE_BYTES(symbol)	(symbol)->ginfo.value.bytes
-#define SYMBOL_VALUE_COMMON_BLOCK(symbol) (symbol)->ginfo.value.common_block
-#define SYMBOL_BLOCK_VALUE(symbol)	(symbol)->ginfo.value.block
-#define SYMBOL_VALUE_CHAIN(symbol)	(symbol)->ginfo.value.chain
-#define SYMBOL_LANGUAGE(symbol)		(symbol)->ginfo.language
-#define SYMBOL_SECTION(symbol)		(symbol)->ginfo.section
+  ((symbol)->value.address = (new_value))
+#define SYMBOL_VALUE_BYTES(symbol)	(symbol)->value.bytes
+#define SYMBOL_VALUE_COMMON_BLOCK(symbol) (symbol)->value.common_block
+#define SYMBOL_BLOCK_VALUE(symbol)	(symbol)->value.block
+#define SYMBOL_VALUE_CHAIN(symbol)	(symbol)->value.chain
+#define SYMBOL_LANGUAGE(symbol)		(symbol)->language
+#define SYMBOL_SECTION(symbol)		(symbol)->section
 #define SYMBOL_OBJ_SECTION(objfile, symbol)			\
-  (((symbol)->ginfo.section >= 0)				\
-   ? (&(((objfile)->sections)[(symbol)->ginfo.section]))	\
+  (((symbol)->section >= 0)				\
+   ? (&(((objfile)->sections)[(symbol)->section]))	\
    : NULL)
 
 /* Initializes the language dependent portion of a symbol
    depending upon the language for the symbol.  */
 #define SYMBOL_SET_LANGUAGE(symbol,language,obstack)	\
-  (symbol_set_language (&(symbol)->ginfo, (language), (obstack)))
+  (symbol_set_language ((symbol), (language), (obstack)))
 extern void symbol_set_language (struct general_symbol_info *symbol,
                                  enum language language,
 				 struct obstack *obstack);
@@ -509,13 +504,13 @@
    be terminated and either already on the objfile's obstack or
    permanently allocated.  */
 #define SYMBOL_SET_LINKAGE_NAME(symbol,linkage_name) \
-  (symbol)->ginfo.name = (linkage_name)
+  (symbol)->name = (linkage_name)
 
 /* Set the linkage and natural names of a symbol, by demangling
    the linkage name.  If linkage_name may not be nullterminated,
    copy_name must be set to true.  */
 #define SYMBOL_SET_NAMES(symbol,linkage_name,copy_name,objfile)	\
-  symbol_set_names (&(symbol)->ginfo, linkage_name, copy_name, \
+  symbol_set_names ((symbol), linkage_name, copy_name, \
 		    (objfile)->per_bfd)
 extern void symbol_set_names (struct general_symbol_info *symbol,
 			      gdb::string_view linkage_name, bool copy_name,
@@ -535,7 +530,7 @@
    demangled name.  */
 
 #define SYMBOL_NATURAL_NAME(symbol) \
-  (symbol_natural_name (&(symbol)->ginfo))
+  (symbol_natural_name ((symbol)))
 extern const char *symbol_natural_name
   (const struct general_symbol_info *symbol);
 
@@ -544,12 +539,12 @@
    manipulation by the linker, this is the mangled name; otherwise,
    it's the same as SYMBOL_NATURAL_NAME.  */
 
-#define SYMBOL_LINKAGE_NAME(symbol)	(symbol)->ginfo.name
+#define SYMBOL_LINKAGE_NAME(symbol)	(symbol)->name
 
 /* Return the demangled name for a symbol based on the language for
    that symbol.  If no demangled name exists, return NULL.  */
 #define SYMBOL_DEMANGLED_NAME(symbol) \
-  (symbol_demangled_name (&(symbol)->ginfo))
+  (symbol_demangled_name ((symbol)))
 extern const char *symbol_demangled_name
   (const struct general_symbol_info *symbol);
 
@@ -560,7 +555,7 @@
    The result should never be NULL.  Don't use this for internal
    purposes (e.g. storing in a hashtable): it's only suitable for output.
 
-   N.B. symbol may be anything with a ginfo member,
+   N.B. symbol may be anything inheriting from general_symbol_info,
    e.g., struct symbol or struct minimal_symbol.  */
 
 #define SYMBOL_PRINT_NAME(symbol)					\
@@ -573,13 +568,13 @@
    name.  If there is no distinct demangled name, then SYMBOL_SEARCH_NAME
    returns the same value (same pointer) as SYMBOL_LINKAGE_NAME.  */
 #define SYMBOL_SEARCH_NAME(symbol)					 \
-   (symbol_search_name (&(symbol)->ginfo))
+   (symbol_search_name (symbol))
 extern const char *symbol_search_name (const struct general_symbol_info *ginfo);
 
 /* Return true if NAME matches the "search" name of SYMBOL, according
    to the symbol's language.  */
 #define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)                       \
-  symbol_matches_search_name (&(symbol)->ginfo, (name))
+  symbol_matches_search_name ((symbol), (name))
 
 /* Helper for SYMBOL_MATCHES_SEARCH_NAME that works with both symbols
    and psymbols.  */
@@ -1105,16 +1100,30 @@
 
 /* This structure is space critical.  See space comments at the top.  */
 
-struct symbol
+struct symbol : public general_symbol_info, public allocate_on_obstack
 {
-
-  /* The general symbol info required for all types of symbols.  */
-
-  struct general_symbol_info ginfo;
+  symbol ()
+    {
+      /* We can't use an initializer list for members of a base class.  */
+      name = nullptr;
+      value.ivalue = 0;
+      language_specific.obstack = nullptr;
+      language = language_unknown;
+      ada_mangled = 0;
+      section = 0;
+      /* Class-initialization of bitfields is only allowed in C++20.  */
+      domain = UNDEF_DOMAIN;
+      aclass_index = 0;
+      is_objfile_owned = 0;
+      is_argument = 0;
+      is_inlined = 0;
+      maybe_copied = 0;
+      subclass = SYMBOL_NONE;
+    }
 
   /* Data type of value */
 
-  struct type *type;
+  struct type *type = nullptr;
 
   /* The owner of this symbol.
      Which one to use is defined by symbol.is_objfile_owned.  */
@@ -1124,7 +1133,7 @@
     /* The symbol table containing this symbol.  This is the file associated
        with LINE.  It can be NULL during symbols read-in but it is never NULL
        during normal operation.  */
-    struct symtab *symtab;
+    struct symtab *symtab = nullptr;
 
     /* For types defined by the architecture.  */
     struct gdbarch *arch;
@@ -1141,7 +1150,7 @@
   unsigned int aclass_index : SYMBOL_ACLASS_BITS;
 
   /* If non-zero then symbol is objfile-owned, use owner.symtab.
-     Otherwise symbol is arch-owned, use owner.arch.  */
+       Otherwise symbol is arch-owned, use owner.arch.  */
 
   unsigned int is_objfile_owned : 1;
 
@@ -1175,7 +1184,7 @@
      to debug files longer than 64K lines?  What about machine
      generated programs?  */
 
-  unsigned short line;
+  unsigned short line = 0;
 
   /* An arbitrary data pointer, allowing symbol readers to record
      additional information on a per-symbol basis.  Note that this data
@@ -1189,9 +1198,9 @@
      to add a magic symbol to the block containing this information,
      or to have a generic debug info annotation slot for symbols.  */
 
-  void *aux_value;
+  void *aux_value = nullptr;
 
-  struct symbol *hash_next;
+  struct symbol *hash_next = nullptr;
 };
 
 /* This struct is size-critical (see comment at the top), so this assert
@@ -1272,11 +1281,11 @@
 struct template_symbol : public symbol
 {
   /* The number of template arguments.  */
-  int n_template_arguments;
+  int n_template_arguments = 0;
 
   /* The template arguments.  This is an array with
      N_TEMPLATE_ARGUMENTS elements.  */
-  struct symbol **template_arguments;
+  struct symbol **template_arguments = nullptr;
 };
 
 /* A symbol that represents a Rust virtual table object.  */
@@ -1285,7 +1294,7 @@
 {
   /* The concrete type for which this vtable was created; that is, in
      "impl Trait for Type", this is "Type".  */
-  struct type *concrete_type;
+  struct type *concrete_type = nullptr;
 };
 
 
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index 20a2187..4ea9b0b 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -1522,7 +1522,7 @@
 }
 
 #define	SYMBOL_DUP(SYMBOL1, SYMBOL2)	\
-  (SYMBOL2) = XOBNEW (&objfile->objfile_obstack, struct symbol); \
+  (SYMBOL2) = new (&objfile->objfile_obstack) symbol (); \
   *(SYMBOL2) = *(SYMBOL1);
 
 
