[v5,3/5] gdb: Migrate frame unwinders to use C++ classes

Message ID 20241001184235.3710608-4-guinevere@redhat.com
State New
Headers
Series Modernize frame unwinders and add disable feature |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Guinevere Larsen Oct. 1, 2024, 6:42 p.m. UTC
  From: Guinevere Larsen <blarsen@redhat.com>

Frame unwinders have historically been a structure populated with
callback pointers, so that architectures (or other specific unwinders)
could install their own way to handle the inferior. However, since
moving to C++, we could use polymorphism to get the same functionality
in a more readable way. Polymorphism also makes it simpler to add new
functionality to all frame unwinders, since all that's required is
adding it to the base class.

As part of the changes to add support to disabling frame unwinders,
this commit makes the first baby step in  using polymorphism for the
frame unwinders, by making frame_unwind a virtual class, and adds a
couple of new classes. The main class added is frame_unwind_legacy,
which works the same as the previous structs, using function pointers
as callbacks. This class was added to allow the transition to happen
piecemeal. New unwinders should instead follow the lead of the other
classes implemented.

2 of the others, frame_unwind_python and frame_unwind_trampoline, were added
because it seemed simpler at the moment to do that instead of reworking
the dynamic allocation to work with the legacy class, and can be used as
an example to future implementations.

Finally, the cygwin unwinder was converted to a class since it was most
of the way there already.
---
 gdb/aarch64-tdep.c          |  10 +--
 gdb/alpha-mdebug-tdep.c     |   5 +-
 gdb/alpha-tdep.c            |  10 +--
 gdb/amd64-obsd-tdep.c       |   5 +-
 gdb/amd64-tdep.c            |  20 ++---
 gdb/amd64-windows-tdep.c    |   5 +-
 gdb/amdgpu-tdep.c           |   6 +-
 gdb/arc-tdep.c              |   8 +-
 gdb/arm-tdep.c              |  24 +++---
 gdb/avr-tdep.c              |   4 +-
 gdb/bfin-tdep.c             |   5 +-
 gdb/bpf-tdep.c              |   5 +-
 gdb/cris-tdep.c             |  10 +--
 gdb/csky-tdep.c             |   8 +-
 gdb/dummy-frame.c           |   7 +-
 gdb/dummy-frame.h           |   2 +-
 gdb/dwarf2/frame-tailcall.c |   5 +-
 gdb/dwarf2/frame-tailcall.h |   2 +-
 gdb/dwarf2/frame.c          |  14 ++--
 gdb/frame-unwind.c          |  61 ++++++++++++++-
 gdb/frame-unwind.h          | 145 +++++++++++++++++++++++++++++++++---
 gdb/frame.c                 |  28 +++----
 gdb/frv-linux-tdep.c        |   5 +-
 gdb/frv-tdep.c              |   4 +-
 gdb/ft32-tdep.c             |   5 +-
 gdb/h8300-tdep.c            |   4 +-
 gdb/hppa-linux-tdep.c       |   4 +-
 gdb/hppa-tdep.c             |  14 ++--
 gdb/i386-obsd-tdep.c        |   4 +-
 gdb/i386-tdep.c             |  25 +++----
 gdb/ia64-tdep.c             |  20 ++---
 gdb/inline-frame.c          |   4 +-
 gdb/inline-frame.h          |   2 +-
 gdb/iq2000-tdep.c           |   4 +-
 gdb/jit.c                   |   5 +-
 gdb/lm32-tdep.c             |   4 +-
 gdb/loongarch-tdep.c        |   6 +-
 gdb/m32c-tdep.c             |   4 +-
 gdb/m32r-linux-tdep.c       |   4 +-
 gdb/m32r-tdep.c             |   4 +-
 gdb/m68hc11-tdep.c          |   4 +-
 gdb/m68k-linux-tdep.c       |   5 +-
 gdb/m68k-tdep.c             |   5 +-
 gdb/mep-tdep.c              |   4 +-
 gdb/microblaze-tdep.c       |   5 +-
 gdb/mips-sde-tdep.c         |   5 +-
 gdb/mips-tdep.c             |  20 ++---
 gdb/mn10300-tdep.c          |   4 +-
 gdb/moxie-tdep.c            |   4 +-
 gdb/msp430-tdep.c           |   4 +-
 gdb/nds32-tdep.c            |  12 ++-
 gdb/nios2-tdep.c            |  10 +--
 gdb/or1k-tdep.c             |   6 +-
 gdb/ppc-fbsd-tdep.c         |   4 +-
 gdb/ppc-obsd-tdep.c         |   4 +-
 gdb/python/py-unwind.c      |  63 ++++++++++------
 gdb/record-btrace.c         |  10 +--
 gdb/record.h                |   4 +-
 gdb/riscv-tdep.c            |   7 +-
 gdb/rl78-tdep.c             |   5 +-
 gdb/rs6000-aix-tdep.c       |   4 +-
 gdb/rs6000-tdep.c           |  10 +--
 gdb/rx-tdep.c               |   8 +-
 gdb/s12z-tdep.c             |   6 +-
 gdb/s390-linux-tdep.c       |   4 +-
 gdb/s390-tdep.c             |   8 +-
 gdb/sentinel-frame.c        |   7 +-
 gdb/sentinel-frame.h        |   2 +-
 gdb/sh-tdep.c               |   9 +--
 gdb/sparc-netbsd-tdep.c     |   5 +-
 gdb/sparc-obsd-tdep.c       |   5 +-
 gdb/sparc-sol2-tdep.c       |   5 +-
 gdb/sparc-tdep.c            |   5 +-
 gdb/sparc64-fbsd-tdep.c     |   5 +-
 gdb/sparc64-netbsd-tdep.c   |   5 +-
 gdb/sparc64-obsd-tdep.c     |  10 +--
 gdb/sparc64-sol2-tdep.c     |   5 +-
 gdb/sparc64-tdep.c          |   5 +-
 gdb/tic6x-tdep.c            |  10 +--
 gdb/tilegx-tdep.c           |   4 +-
 gdb/tramp-frame.c           |  76 +++++++++++++------
 gdb/v850-tdep.c             |   4 +-
 gdb/vax-tdep.c              |   5 +-
 gdb/windows-tdep.c          |  33 ++++----
 gdb/windows-tdep.h          |  16 +++-
 gdb/xstormy16-tdep.c        |   4 +-
 gdb/xtensa-tdep.c           |   6 +-
 gdb/z80-tdep.c              |   6 +-
 88 files changed, 564 insertions(+), 404 deletions(-)
  

Comments

Thiago Jung Bauermann Oct. 3, 2024, 12:23 a.m. UTC | #1
This looks great, thank you for considering and incorporating my
suggestions. I agree that a number of them would make the diff harder to
review and can be considered future improvements. But there are two
changes which IMHO don't change the diff very much and are important
when converting to C++. More below.

Also a couple of formatting nits.

Guinevere Larsen <guinevere@redhat.com> writes:

> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
> index afc1258c6a9..7be01ec2203 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -332,6 +332,59 @@ frame_unwind_got_address (const frame_info_ptr &frame, int regnum,
>    return reg_val;
>  }
>  
> +/* This method just passes the parameters to the callback pointer.  */
> +enum unwind_stop_reason
> +frame_unwind_legacy::stop_reason (const frame_info_ptr &this_frame,
> +				  void **this_prologue_cache) const
> +{
> +  return stop_reason_p (this_frame, this_prologue_cache);
> +}
> +
> +/* This method just passes the parameters to the callback pointer.  */
> +void
> +frame_unwind_legacy::this_id (const frame_info_ptr &this_frame,
> +			      void **this_prologue_cache,
> +			      struct frame_id *id) const
> +{
> +  return this_id_p (this_frame, this_prologue_cache, id);
> +}
> +
> +/* This method just passes the parameters to the callback pointer.  */
> +struct value *
> +frame_unwind_legacy::prev_register (const frame_info_ptr &this_frame,
> +				    void **this_prologue_cache,
> +				    int regnum) const
> +{
> +  return prev_register_p (this_frame, this_prologue_cache, regnum);
> +}
> +
> +/* This method just passes the parameters to the callback pointer.  */
> +int
> +frame_unwind_legacy::sniffer (const struct frame_unwind *self,
> +			      const frame_info_ptr &this_frame,
> +			      void **this_prologue_cache) const
> +{
> +  return sniffer_p (self, this_frame, this_prologue_cache);
> +}
> +
> +/* This method just passes the parameters to the callback pointer.  */
> +void
> +frame_unwind_legacy::dealloc_cache (frame_info *self, void *this_cache) const
> +{
> +  if (dealloc_cache_p != nullptr)
> +    dealloc_cache_p (self, this_cache);
> +}
> +
> +/* This method just passes the parameters to the callback pointer.  */
> +struct gdbarch *
> +frame_unwind_legacy::prev_arch (const frame_info_ptr &this_frame,
> +				void **this_prologue_cache) const
> +{
> +  if (prev_arch_p == nullptr)
> +    error (_("No prev_arch callback installed"));
> +  return prev_arch_p (this_frame, this_prologue_cache);
> +}

As I mentioned before, changing this to:

struct gdbarch *
frame_unwind_legacy::prev_arch (const frame_info_ptr &this_frame,
				void **this_prologue_cache) const
{
  if (prev_arch_p == nullptr)
    return frame_unwind::prev_arch (this_frame, this_prologue_cache);
  return prev_arch_p (this_frame, this_prologue_cache);
}

and frame_unwind::prev_arch () to:

virtual struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
				   void **this_prologue_cache) const
{
  return get_frame_arch (this_frame);
}

Allows calling this method without the try/catch in frame_unwind_arch
().  When class inheritance provides an adequate solution to method
dispatch, I don't see a reason to throw and catch an exception to
accomplish the same task.

Also, the diff is simple:

https://git.linaro.org/people/thiago.bauermann/binutils-gdb.git/commit/?h=review/modernize-frame-unwinders&id=88db6b3f96c3afe7b3f45525b7f786d14a89bbc5

And (I could be wrong of course but) I don't see a risk for regressions,
so I don't see a need to leave this change for a future patch.

> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
> index deab4f7dbfb..b078945a200 100644
> --- a/gdb/frame-unwind.h
> +++ b/gdb/frame-unwind.h
> @@ -169,25 +169,148 @@ enum frame_unwind_class
>    FRAME_UNWIND_ARCH,
>  };
>  
> -struct frame_unwind
> +class frame_unwind
>  {
> -  const char *name;
> +  const char *m_name;
>    /* The frame's type.  Should this instead be a collection of
>       predicates that test the frame for various attributes?  */
> -  enum frame_type type;
> +  enum frame_type m_type;
>    /* What kind of unwinder is this.  It generally follows from where
>       the unwinder was added or where it looks for information to do the
>       unwinding.  */
> -  enum frame_unwind_class unwinder_class;
> +  enum frame_unwind_class m_unwinder_class;
> +  const struct frame_data *m_unwind_data;
> +public:
> +  frame_unwind (const char *name, frame_type type, frame_unwind_class uclass,
> +		       const struct frame_data *data)
> +    : m_name (name), m_type (type), m_unwinder_class (uclass),
> +      m_unwind_data (data)
> +    { }
> +
> +  const char *name () const
> +  {
> +    return m_name;
> +  }
> +
> +  enum frame_type type () const
> +  {
> +    return m_type;
> +  }
> +
> +  enum frame_unwind_class unwinder_class () const
> +  {
> +    return m_unwinder_class;
> +  }
> +
> +  const struct frame_data *unwind_data () const
> +  {
> +    return m_unwind_data;
> +  }
> +
> +  /* Default stop_reason function.  It reports NO_REASON, unless the
> +     frame is the outermost.  */
> +  virtual enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
> +					       void **this_prologue_cache) const
> +  {
> +    return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
> +  }
> +
> +  /* Default frame sniffer.  Will always return that the unwinder
> +     is able to unwind the frame.  */
> +  virtual int sniffer (const frame_unwind *self,
> +		       const frame_info_ptr &this_frame,
> +		       void **this_prologue_cache) const
> +  {
> +    return 1;
> +  }

The other thing I believe should be addressed in a C++ conversion is to
avoid having a method argument which duplicates the implicit 'this'
argument, as is the case of the 'self' argument in the sniffer ()
method.

Fortunately the diff is also small:

https://git.linaro.org/people/thiago.bauermann/binutils-gdb.git/commit/?h=review/modernize-frame-unwinders&id=9555b3ae4feea0f4ff15028257716ff2c3c42732

So IMHO it can be done in this patch.

Also, a nit: the lines starting with "const ..." and "void ..." above
are unaligned.

> diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c
> index 6368f67a2e7..b1f3be66bc1 100644
> --- a/gdb/tramp-frame.c
> +++ b/gdb/tramp-frame.c
> @@ -57,10 +57,45 @@ tramp_frame_cache (const frame_info_ptr &this_frame,
>    return tramp_cache->trad_cache;
>  }
>  
> -static void
> -tramp_frame_this_id (const frame_info_ptr &this_frame,
> -		     void **this_cache,
> -		     struct frame_id *this_id)
> +class frame_unwind_trampoline : public frame_unwind
> +{
> +private:
> +  frame_prev_arch_ftype *prev_arch_p;
> +public:
> +  frame_unwind_trampoline (enum frame_type t, const struct frame_data *d,
> +			   frame_prev_arch_ftype *pa)
> +    : frame_unwind ("trampoline", t, FRAME_UNWIND_GDB, d), prev_arch_p (pa)
> +  { }
> +
> +  int sniffer(const frame_unwind *self, const frame_info_ptr &this_frame,
> +	      void **this_prologue_cache) const override;

Space before '('.

> +
> +  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
> +		struct frame_id *id) const override;
> +
> +  struct value *prev_register (const frame_info_ptr &this_frame,
> +			       void **this_prologue_cache,
> +			       int regnum) const override;
> +
> +  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
> +			     void **this_prologue_cache) const override
> +  {
> +    if (prev_arch_p == nullptr)
> +      error (_("No prev_arch callback installed"));
> +    return prev_arch_p (this_frame, this_prologue_cache);
> +  }

With the prev_arch () changes I'm suggesting, this error () can be
changed to internal_error ().

> +
> +  /* FIXME: This should have a proper algorithm to deallocate the cache,
> +     otherwise memory is leaked.  This method is empty here just so the
> +     migration to c++ classes doesn't add regressions.  */
> +  void dealloc_cache (frame_info *self, void *this_cache) const override
> +  { }
> +};
  
Simon Marchi Oct. 3, 2024, 8:06 p.m. UTC | #2
> @@ -332,6 +332,59 @@ frame_unwind_got_address (const frame_info_ptr &frame, int regnum,
>    return reg_val;
>  }
>  
> +/* This method just passes the parameters to the callback pointer.  */
> +enum unwind_stop_reason
> +frame_unwind_legacy::stop_reason (const frame_info_ptr &this_frame,
> +				  void **this_prologue_cache) const

Since these methods are already documented in the .h, the comment should
just say:

/* See frame-unwind.h.  */

> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
> index deab4f7dbfb..b078945a200 100644
> --- a/gdb/frame-unwind.h
> +++ b/gdb/frame-unwind.h
> @@ -169,25 +169,148 @@ enum frame_unwind_class
>    FRAME_UNWIND_ARCH,
>  };
>  
> -struct frame_unwind
> +class frame_unwind
>  {
> -  const char *name;
> +  const char *m_name;
>    /* The frame's type.  Should this instead be a collection of
>       predicates that test the frame for various attributes?  */
> -  enum frame_type type;
> +  enum frame_type m_type;
>    /* What kind of unwinder is this.  It generally follows from where
>       the unwinder was added or where it looks for information to do the
>       unwinding.  */
> -  enum frame_unwind_class unwinder_class;
> +  enum frame_unwind_class m_unwinder_class;
> +  const struct frame_data *m_unwind_data;

To be consistent, please move the private fields at the end, like we do elsewhere.

> +public:
> +  frame_unwind (const char *name, frame_type type, frame_unwind_class uclass,
> +		       const struct frame_data *data)
> +    : m_name (name), m_type (type), m_unwinder_class (uclass),
> +      m_unwind_data (data)
> +    { }
> +
> +  const char *name () const
> +  {
> +    return m_name;
> +  }

If you want to save some lines, we use this style elsewhere for trivial
getters:

const char *name () const
{ return m_name; }

I don't really mind if you prefer not, we currently have both styles in
the code base...

> +
> +  enum frame_type type () const
> +  {
> +    return m_type;
> +  }
> +
> +  enum frame_unwind_class unwinder_class () const

To be more C++-y, I would suggest dropping enum/struct/class keywords
where possible, for instance the return value above.

> +  {
> +    return m_unwinder_class;
> +  }
> +
> +  const struct frame_data *unwind_data () const
> +  {
> +    return m_unwind_data;
> +  }
> +
> +  /* Default stop_reason function.  It reports NO_REASON, unless the
> +     frame is the outermost.  */

function -> method, I would say.  Or "Default stop_reason
implementation"

> +  virtual enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
> +					       void **this_prologue_cache) const
> +  {
> +    return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
> +  }
> +
> +  /* Default frame sniffer.  Will always return that the unwinder
> +     is able to unwind the frame.  */
> +  virtual int sniffer (const frame_unwind *self,

I agree with Thiago's comment about not passing "self" (unless there's
something I don't see here).

> +		       const frame_info_ptr &this_frame,
> +		       void **this_prologue_cache) const
> +  {
> +    return 1;
> +  }
> +
> +  /* Calculate the ID of the given frame using this unwinder.  */
> +  virtual void this_id (const frame_info_ptr &this_frame,
> +			void **this_prologue_cache,
> +			struct frame_id *id) const = 0;

This is pre-existing code so it makes sense to have it like that for the
moment, but I don't see why this has to return the value by parameter,
surely the method could return a frame_id directly?

> +
> +  /* Get the value of a register in a previous frame.  */
> +  virtual struct value *prev_register (const frame_info_ptr &this_frame,
> +				       void **this_prologue_cache,
> +				       int regnum) const = 0;
> +
> +  /* The following methods are here mostly for interface functionality.  They
> +     all throw an error when called, as a safe way to check if an unwinder has
> +     implemented the desired functionality.  */
> +
> +  /* Properly deallocate the cache.  */
> +  virtual void dealloc_cache (frame_info *self, void *this_cache) const
> +  {
> +    internal_error (_("No method dealloc_cache implemented for unwinder %s"),
> +		    m_name);

I think a reasonable default implementation here would be to do nothing.
After all, that's what the current code (function frame_info_del) does:
if the callback is nullptr, nothing is done.  An implementation that
doesn't use any frame cache (is there any?) will just be able to omit
this method.

> +  }
> +
> +  /* Get the previous architecture.  */
> +  virtual struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
> +				     void **this_prologue_cache) const
> +  {
> +    error (_("No method prev_arch implemented for unwinder %s"), m_name);

I think Thiago's suggestion makes sense, a default implementation of
returning this_frame's arch would be reasonable.  It is by far the most
common and intuitive case that the arch for a given frame will be the
same as the arch for the frame it got unwound from.  Only the few
implementations that want to do something different (e.g. sentinel) can
implement it.


> +  }
> +};
> +
> +/* This is a legacy version of the frame unwinder.  The original struct
> +   used function pointers for callbacks, this updated version has a
> +   method that just passes the parameters along to the callback
> +   pointer.
> +   Do not use this class for new unwinders.  Instead, see other classes
> +   that inherit from frame_unwind, such as the python unwinder.  */
> +class frame_unwind_legacy : public frame_unwind
> +{
> +private:
>    /* Should an attribute indicating the frame's address-in-block go
>       here?  */
> -  frame_unwind_stop_reason_ftype *stop_reason;
> -  frame_this_id_ftype *this_id;
> -  frame_prev_register_ftype *prev_register;
> -  const struct frame_data *unwind_data;
> -  frame_sniffer_ftype *sniffer;
> -  frame_dealloc_cache_ftype *dealloc_cache;
> -  frame_prev_arch_ftype *prev_arch;
> +  frame_unwind_stop_reason_ftype *stop_reason_p;
> +  frame_this_id_ftype *this_id_p;
> +  frame_prev_register_ftype *prev_register_p;
> +  frame_sniffer_ftype *sniffer_p;
> +  frame_dealloc_cache_ftype *dealloc_cache_p;
> +  frame_prev_arch_ftype *prev_arch_p;

Move the private fields at the end, and prefix with `m_`.  Also, in GNU
code the `_p` prefix usually means "predicate", so usually for boolean
values or functions returning booleans.  I guess here with `_p` you
meant "pointer".  Just doing with "m_stop_reason" & co would be fine I
think, it's not worse than what was there previously.

> diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c
> index 6368f67a2e7..b1f3be66bc1 100644
> --- a/gdb/tramp-frame.c
> +++ b/gdb/tramp-frame.c
> @@ -57,10 +57,45 @@ tramp_frame_cache (const frame_info_ptr &this_frame,
>    return tramp_cache->trad_cache;
>  }
>  
> -static void
> -tramp_frame_this_id (const frame_info_ptr &this_frame,
> -		     void **this_cache,
> -		     struct frame_id *this_id)
> +class frame_unwind_trampoline : public frame_unwind
> +{
> +private:
> +  frame_prev_arch_ftype *prev_arch_p;

Please move this at the end of the class.  And I suggest naming it
"m_prev_arch", for the same reason as explained above.

> +public:
> +  frame_unwind_trampoline (enum frame_type t, const struct frame_data *d,
> +			   frame_prev_arch_ftype *pa)
> +    : frame_unwind ("trampoline", t, FRAME_UNWIND_GDB, d), prev_arch_p (pa)
> +  { }

This "frame_data" thing appears to be some kind of opaque / private data
that frame_unwind implementations can use?  For those that you converted
to proper classes (trampoline and python), could that data be stored in
the frame_unwind_* object directly?  It would remove some unnecessary
indirections and make things simpler, I think.

Actually, I gave it a try, and trampoline and python are the only
implementations actually using the frame_data pointer.  So if they stop
using it, we can just get rid of that.  That requires touching all the
frame_unwind_legacy constructor call sites, but I was able to do that
relatively easily with a global regex find & replace in VScode. [last
minute edit: I just realized that I was building without
--enable-targets=all, so take this with a grain of salt]

> +
> +  int sniffer(const frame_unwind *self, const frame_info_ptr &this_frame,

Missing space.

> +	      void **this_prologue_cache) const override;
> +
> +  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
> +		struct frame_id *id) const override;
> +
> +  struct value *prev_register (const frame_info_ptr &this_frame,
> +			       void **this_prologue_cache,
> +			       int regnum) const override;
> +
> +  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
> +			     void **this_prologue_cache) const override
> +  {
> +    if (prev_arch_p == nullptr)
> +      error (_("No prev_arch callback installed"));
> +    return prev_arch_p (this_frame, this_prologue_cache);
> +  }
> +
> +  /* FIXME: This should have a proper algorithm to deallocate the cache,
> +     otherwise memory is leaked.  This method is empty here just so the
> +     migration to c++ classes doesn't add regressions.  */
> +  void dealloc_cache (frame_info *self, void *this_cache) const override
> +  { }

From what I can see, this unwinder uses the "trad frame" cache, which
allocates everything on the frame_cache_obstack obstack, which gets
cleared in reinit_frame_cache().  So I think this unwinder could just
omit the dealloc_cache method, as it would not need it.

> @@ -161,16 +196,11 @@ tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,
>    gdb_assert (tramp_frame->insn_size <= sizeof (tramp_frame->insn[0].bytes));
>  
>    data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data);
> -  unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind);
> -
>    data->tramp_frame = tramp_frame;
> -  unwinder->type = tramp_frame->frame_type;
> -  unwinder->unwind_data = data;
> -  unwinder->unwinder_class = FRAME_UNWIND_GDB;
> -  unwinder->sniffer = tramp_frame_sniffer;
> -  unwinder->stop_reason = default_frame_unwind_stop_reason;
> -  unwinder->this_id = tramp_frame_this_id;
> -  unwinder->prev_register = tramp_frame_prev_register;
> -  unwinder->prev_arch = tramp_frame->prev_arch;
> +
> +  unwinder = obstack_new <frame_unwind_trampoline> (gdbarch_obstack (gdbarch),
> +						    tramp_frame->frame_type,
> +						    data,
> +						    tramp_frame->prev_arch);

Here, given that `data` points to the `frame_data` object, which
contains a pointer to the `tramp_frame`, I think it's redundant to pass
that in addition to `frame_type` and `prev_arch`.  I think you could
replace all that by a single argument of type `tramp_frame *` (and store
that pointer directly in the `frame_unwind_trampoline` object, instead
of passing through that `frame_data` thing).

Simon
  
Simon Marchi Oct. 4, 2024, 5:21 a.m. UTC | #3
Some more things I thought about...

>> +  /* Default frame sniffer.  Will always return that the unwinder
>> +     is able to unwind the frame.  */
>> +  virtual int sniffer (const frame_unwind *self,

One nit: I think this method should be called just "sniff".  When a
method is named after a thing, my brain adds an implicit "get_" in front
of it.  So to me, a method named "sniffer" would return some sniffer
object.  Here, I think it would make more sense to use an action verb,
because we want the unwinder to "sniff".

Oh and I guess another naming nit, I think frame_unwind should be named
frame_unwinder.  A "thing" that unwinds stuff would be called an
"unwinder", not an "unwind".  I don't know how you feel about changing
that.

And something else I just remembered, I am not really a fan of the
very abbreviated parameter names like this:

  frame_unwind_trampoline (enum frame_type t, const struct frame_data *d,
			   frame_prev_arch_ftype *pa)
    : frame_unwind ("trampoline", t, FRAME_UNWIND_GDB, d), prev_arch_p (pa)
  { }

When using an editor that provides tooltips when writing a function
call, the abbreviated names in the tooltip are not very helpful.  I
would suggest using proper names.

>> +public:
>> +  frame_unwind_trampoline (enum frame_type t, const struct frame_data *d,
>> +			   frame_prev_arch_ftype *pa)
>> +    : frame_unwind ("trampoline", t, FRAME_UNWIND_GDB, d), prev_arch_p (pa)
>> +  { }
> 
> This "frame_data" thing appears to be some kind of opaque / private data
> that frame_unwind implementations can use?  For those that you converted
> to proper classes (trampoline and python), could that data be stored in
> the frame_unwind_* object directly?  It would remove some unnecessary
> indirections and make things simpler, I think.
> 
> Actually, I gave it a try, and trampoline and python are the only
> implementations actually using the frame_data pointer.  So if they stop
> using it, we can just get rid of that.  That requires touching all the
> frame_unwind_legacy constructor call sites, but I was able to do that
> relatively easily with a global regex find & replace in VScode. [last
> minute edit: I just realized that I was building without
> --enable-targets=all, so take this with a grain of salt]

One more thought about all this: in an ideal world, the tramp_frame uses
should probably become sub-classes of frame_unwind_trampoline.  Only
those sites that want to override the prev_arch method would need to do
it (AFAIK, the only one is aarch64_linux_rt_sigframe).  The `init` and
`validate` callbacks in `tramp_frame` would become virtual methods of
`frame_unwind_trampoline`, and the other data fields of `tramp_frame`
would become fields of `frame_unwind_trampoline`.  I started to
prototype it on top of this patch, and it would be quite a lot of work.
So let's keep that on the wishlist for now.

Simon
  

Patch

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 3989c619e0b..39cdcb2fc29 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1205,8 +1205,7 @@  aarch64_prologue_prev_register (const frame_info_ptr &this_frame,
 }
 
 /* AArch64 prologue unwinder.  */
-static frame_unwind aarch64_prologue_unwind =
-{
+static const frame_unwind_legacy aarch64_prologue_unwind (
   "aarch64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1215,7 +1214,7 @@  static frame_unwind aarch64_prologue_unwind =
   aarch64_prologue_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Allocate and fill in *THIS_CACHE with information about the prologue of
    *THIS_FRAME.  Do not do this is if *THIS_CACHE was already allocated.
@@ -1301,8 +1300,7 @@  aarch64_stub_unwind_sniffer (const struct frame_unwind *self,
 }
 
 /* AArch64 stub unwinder.  */
-static frame_unwind aarch64_stub_unwind =
-{
+static const frame_unwind_legacy aarch64_stub_unwind (
   "aarch64 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1311,7 +1309,7 @@  static frame_unwind aarch64_stub_unwind =
   aarch64_prologue_prev_register,
   NULL,
   aarch64_stub_unwind_sniffer
-};
+);
 
 /* Return the frame base address of *THIS_FRAME.  */
 
diff --git a/gdb/alpha-mdebug-tdep.c b/gdb/alpha-mdebug-tdep.c
index b087afabae7..b43849c203d 100644
--- a/gdb/alpha-mdebug-tdep.c
+++ b/gdb/alpha-mdebug-tdep.c
@@ -330,8 +330,7 @@  alpha_mdebug_frame_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
-static const struct frame_unwind alpha_mdebug_frame_unwind =
-{
+static const struct frame_unwind_legacy alpha_mdebug_frame_unwind (
   "alpha mdebug",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -340,7 +339,7 @@  static const struct frame_unwind alpha_mdebug_frame_unwind =
   alpha_mdebug_frame_prev_register,
   NULL,
   alpha_mdebug_frame_sniffer
-};
+);
 
 static CORE_ADDR
 alpha_mdebug_frame_base_address (const frame_info_ptr &this_frame,
diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c
index f55bf2c8661..d719a575a86 100644
--- a/gdb/alpha-tdep.c
+++ b/gdb/alpha-tdep.c
@@ -1007,8 +1007,7 @@  alpha_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind alpha_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy alpha_sigtramp_frame_unwind (
   "alpha sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1017,7 +1016,7 @@  static const struct frame_unwind alpha_sigtramp_frame_unwind =
   alpha_sigtramp_frame_prev_register,
   NULL,
   alpha_sigtramp_frame_sniffer
-};
+);
 
 
 
@@ -1427,8 +1426,7 @@  alpha_heuristic_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind alpha_heuristic_frame_unwind =
-{
+static const struct frame_unwind_legacy alpha_heuristic_frame_unwind (
   "alpha prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1437,7 +1435,7 @@  static const struct frame_unwind alpha_heuristic_frame_unwind =
   alpha_heuristic_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 alpha_heuristic_frame_base_address (const frame_info_ptr &this_frame,
diff --git a/gdb/amd64-obsd-tdep.c b/gdb/amd64-obsd-tdep.c
index 5359959e3bb..d1ebb0693e6 100644
--- a/gdb/amd64-obsd-tdep.c
+++ b/gdb/amd64-obsd-tdep.c
@@ -402,8 +402,7 @@  amd64obsd_trapframe_sniffer (const struct frame_unwind *self,
 		   || (startswith (name, "Xintr"))));
 }
 
-static const struct frame_unwind amd64obsd_trapframe_unwind =
-{
+static const struct frame_unwind_legacy amd64obsd_trapframe_unwind (
   /* FIXME: kettenis/20051219: This really is more like an interrupt
      frame, but SIGTRAMP_FRAME would print <signal handler called>,
      which really is not what we want here.  */
@@ -415,7 +414,7 @@  static const struct frame_unwind amd64obsd_trapframe_unwind =
   amd64obsd_trapframe_prev_register,
   NULL,
   amd64obsd_trapframe_sniffer
-};
+);
 
 
 static void
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 0701424e5a5..cf6c7e77723 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -2666,8 +2666,7 @@  amd64_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind amd64_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_frame_unwind (
   "amd64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2676,7 +2675,7 @@  static const struct frame_unwind amd64_frame_unwind =
   amd64_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Generate a bytecode expression to get the value of the saved PC.  */
 
@@ -2813,8 +2812,7 @@  amd64_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind amd64_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_sigtramp_frame_unwind (
   "amd64 sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2823,7 +2821,7 @@  static const struct frame_unwind amd64_sigtramp_frame_unwind =
   amd64_sigtramp_frame_prev_register,
   NULL,
   amd64_sigtramp_frame_sniffer
-};
+);
 
 
 static CORE_ADDR
@@ -3006,8 +3004,7 @@  amd64_epilogue_frame_this_id (const frame_info_ptr &this_frame,
     (*this_id) = frame_id_build (cache->base + 16, cache->pc);
 }
 
-static const struct frame_unwind amd64_epilogue_override_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_epilogue_override_frame_unwind (
   "amd64 epilogue override",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3016,10 +3013,9 @@  static const struct frame_unwind amd64_epilogue_override_frame_unwind =
   amd64_frame_prev_register,
   NULL,
   amd64_epilogue_override_frame_sniffer
-};
+);
 
-static const struct frame_unwind amd64_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_epilogue_frame_unwind (
   "amd64 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3028,7 +3024,7 @@  static const struct frame_unwind amd64_epilogue_frame_unwind =
   amd64_frame_prev_register,
   NULL, 
   amd64_epilogue_frame_sniffer
-};
+);
 
 static struct frame_id
 amd64_dummy_id (struct gdbarch *gdbarch, const frame_info_ptr &this_frame)
diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c
index 2e3996b7b47..ad340024a83 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -1181,8 +1181,7 @@  amd64_windows_frame_this_id (const frame_info_ptr &this_frame, void **this_cache
 
 /* Windows x64 SEH unwinder.  */
 
-static const struct frame_unwind amd64_windows_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_windows_frame_unwind (
   "amd64 windows",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1191,7 +1190,7 @@  static const struct frame_unwind amd64_windows_frame_unwind =
   &amd64_windows_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Implement the "skip_prologue" gdbarch method.  */
 
diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c
index b8a5fd80fa0..3272d24a820 100644
--- a/gdb/amdgpu-tdep.c
+++ b/gdb/amdgpu-tdep.c
@@ -889,7 +889,7 @@  amdgpu_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const frame_unwind amdgpu_frame_unwind = {
+static const frame_unwind_legacy amdgpu_frame_unwind (
   "amdgpu",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -899,8 +899,8 @@  static const frame_unwind amdgpu_frame_unwind = {
   nullptr,
   default_frame_sniffer,
   nullptr,
-  nullptr,
-};
+  nullptr
+);
 
 static int
 print_insn_amdgpu (bfd_vma memaddr, struct disassemble_info *info)
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index ac77fcd7a3b..fa97787d038 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -1910,7 +1910,7 @@  arc_sigtramp_frame_sniffer (const struct frame_unwind *self,
    the fallback unwinder, we use the default frame sniffer, which always
    accepts the frame.  */
 
-static const struct frame_unwind arc_frame_unwind = {
+static const struct frame_unwind_legacy arc_frame_unwind (
   "arc prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1921,13 +1921,13 @@  static const struct frame_unwind arc_frame_unwind = {
   default_frame_sniffer,
   NULL,
   NULL
-};
+);
 
 /* Structure defining the ARC signal frame unwind functions.  Custom
    sniffer is used, because this frame must be accepted only in the right
    context.  */
 
-static const struct frame_unwind arc_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy arc_sigtramp_frame_unwind (
   "arc sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1938,7 +1938,7 @@  static const struct frame_unwind arc_sigtramp_frame_unwind = {
   arc_sigtramp_frame_sniffer,
   NULL,
   NULL
-};
+);
 
 
 static const struct frame_base arc_normal_base = {
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 3818716586b..5b2cf91def3 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -2466,7 +2466,7 @@  arm_prologue_prev_register (const frame_info_ptr &this_frame,
 				       prev_regnum);
 }
 
-static frame_unwind arm_prologue_unwind = {
+static const frame_unwind_legacy arm_prologue_unwind (
   "arm prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2475,7 +2475,7 @@  static frame_unwind arm_prologue_unwind = {
   arm_prologue_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Maintain a list of ARM exception table entries per objfile, similar to the
    list of mapping symbols.  We only cache entries for standard ARM-defined
@@ -3186,7 +3186,7 @@  arm_exidx_unwind_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
-struct frame_unwind arm_exidx_unwind = {
+struct frame_unwind_legacy arm_exidx_unwind (
   "arm exidx",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3195,7 +3195,7 @@  struct frame_unwind arm_exidx_unwind = {
   arm_prologue_prev_register,
   NULL,
   arm_exidx_unwind_sniffer
-};
+);
 
 static struct arm_prologue_cache *
 arm_make_epilogue_frame_cache (const frame_info_ptr &this_frame)
@@ -3296,8 +3296,7 @@  arm_epilogue_frame_sniffer (const struct frame_unwind *self,
 
 /* Frame unwinder from epilogue.  */
 
-static const struct frame_unwind arm_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy arm_epilogue_frame_unwind (
   "arm epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3305,8 +3304,8 @@  static const struct frame_unwind arm_epilogue_frame_unwind =
   arm_epilogue_frame_this_id,
   arm_epilogue_frame_prev_register,
   NULL,
-  arm_epilogue_frame_sniffer,
-};
+  arm_epilogue_frame_sniffer
+);
 
 /* Recognize GCC's trampoline for thumb call-indirect.  If we are in a
    trampoline, return the target PC.  Otherwise return 0.
@@ -3427,7 +3426,7 @@  arm_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-struct frame_unwind arm_stub_unwind = {
+struct frame_unwind_legacy arm_stub_unwind (
   "arm stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3436,7 +3435,7 @@  struct frame_unwind arm_stub_unwind = {
   arm_prologue_prev_register,
   NULL,
   arm_stub_unwind_sniffer
-};
+);
 
 /* Put here the code to store, into CACHE->saved_regs, the addresses
    of the saved registers of frame described by THIS_FRAME.  CACHE is
@@ -3953,8 +3952,7 @@  arm_m_exception_unwind_sniffer (const struct frame_unwind *self,
 /* Frame unwinder for M-profile exceptions (EXC_RETURN on stack),
    lockup and secure/nonsecure interstate function calls (FNC_RETURN).  */
 
-struct frame_unwind arm_m_exception_unwind =
-{
+struct frame_unwind_legacy arm_m_exception_unwind (
   "arm m exception lockup sec_fnc",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3963,7 +3961,7 @@  struct frame_unwind arm_m_exception_unwind =
   arm_m_exception_prev_register,
   NULL,
   arm_m_exception_unwind_sniffer
-};
+);
 
 static CORE_ADDR
 arm_normal_frame_base (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c
index 08b3cb146f4..b3fe3fef759 100644
--- a/gdb/avr-tdep.c
+++ b/gdb/avr-tdep.c
@@ -1155,7 +1155,7 @@  avr_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind avr_frame_unwind = {
+static const struct frame_unwind_legacy avr_frame_unwind (
   "avr prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1164,7 +1164,7 @@  static const struct frame_unwind avr_frame_unwind = {
   avr_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 avr_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c
index 60838f0548e..78ade80ad25 100644
--- a/gdb/bfin-tdep.c
+++ b/gdb/bfin-tdep.c
@@ -372,8 +372,7 @@  bfin_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind bfin_frame_unwind =
-{
+static const struct frame_unwind_legacy bfin_frame_unwind (
   "bfin prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -382,7 +381,7 @@  static const struct frame_unwind bfin_frame_unwind =
   bfin_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Check for "[--SP] = <reg>;" insns.  These are appear in function
    prologues to save misc registers onto the stack.  */
diff --git a/gdb/bpf-tdep.c b/gdb/bpf-tdep.c
index 1f53d63c982..8f3e50d86c1 100644
--- a/gdb/bpf-tdep.c
+++ b/gdb/bpf-tdep.c
@@ -181,8 +181,7 @@  bpf_frame_prev_register (const frame_info_ptr &this_frame,
 
 /* Frame unwinder machinery for BPF.  */
 
-static const struct frame_unwind bpf_frame_unwind =
-{
+static const struct frame_unwind_legacy bpf_frame_unwind (
   "bpf prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -191,7 +190,7 @@  static const struct frame_unwind bpf_frame_unwind =
   bpf_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 
 /* Breakpoints.  */
diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c
index f532c41d261..2d76b963319 100644
--- a/gdb/cris-tdep.c
+++ b/gdb/cris-tdep.c
@@ -435,8 +435,7 @@  cris_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind cris_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy cris_sigtramp_frame_unwind (
   "cris sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -445,7 +444,7 @@  static const struct frame_unwind cris_sigtramp_frame_unwind =
   cris_sigtramp_frame_prev_register,
   NULL,
   cris_sigtramp_frame_sniffer
-};
+);
 
 static int
 crisv32_single_step_through_delay (struct gdbarch *gdbarch,
@@ -901,8 +900,7 @@  cris_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   return sp;
 }
 
-static const struct frame_unwind cris_frame_unwind = 
-{
+static const struct frame_unwind_legacy cris_frame_unwind (
   "cris prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -911,7 +909,7 @@  static const struct frame_unwind cris_frame_unwind =
   cris_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 cris_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/csky-tdep.c b/gdb/csky-tdep.c
index 6e8426fe2d8..4f41a51b576 100644
--- a/gdb/csky-tdep.c
+++ b/gdb/csky-tdep.c
@@ -2159,7 +2159,7 @@  csky_frame_prev_register (const frame_info_ptr &this_frame,
 /* Data structures for the normal prologue-analysis-based
    unwinder.  */
 
-static const struct frame_unwind csky_unwind_cache = {
+static const struct frame_unwind_legacy csky_unwind_cache (
   "cski prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2170,7 +2170,7 @@  static const struct frame_unwind csky_unwind_cache = {
   default_frame_sniffer,
   NULL,
   NULL
-};
+);
 
 static CORE_ADDR
 csky_check_long_branch (const frame_info_ptr &frame, CORE_ADDR pc)
@@ -2294,7 +2294,7 @@  csky_stub_prev_register (const frame_info_ptr &this_frame,
 				       prev_regnum);
 }
 
-static frame_unwind csky_stub_unwind = {
+static const frame_unwind_legacy csky_stub_unwind (
   "csky stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2303,7 +2303,7 @@  static frame_unwind csky_stub_unwind = {
   csky_stub_prev_register,
   NULL,
   csky_stub_unwind_sniffer
-};
+);
 
 /* Implement the this_base, this_locals, and this_args hooks
    for the normal unwinder.  */
diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c
index e7de15b5c4d..697cc11a184 100644
--- a/gdb/dummy-frame.c
+++ b/gdb/dummy-frame.c
@@ -375,8 +375,7 @@  dummy_frame_this_id (const frame_info_ptr &this_frame,
   (*this_id) = cache->this_id;
 }
 
-const struct frame_unwind dummy_frame_unwind =
-{
+const struct frame_unwind_legacy dummy_frame_unwind (
   "dummy",
   DUMMY_FRAME,
   FRAME_UNWIND_GDB,
@@ -384,8 +383,8 @@  const struct frame_unwind dummy_frame_unwind =
   dummy_frame_this_id,
   dummy_frame_prev_register,
   NULL,
-  dummy_frame_sniffer,
-};
+  dummy_frame_sniffer
+);
 
 /* See dummy-frame.h.  */
 
diff --git a/gdb/dummy-frame.h b/gdb/dummy-frame.h
index 7d963ad9beb..a1341797acd 100644
--- a/gdb/dummy-frame.h
+++ b/gdb/dummy-frame.h
@@ -54,7 +54,7 @@  extern void dummy_frame_discard (frame_id dummy_id, thread_info *thread);
 /* If the PC falls in a dummy frame, return a dummy frame
    unwinder.  */
 
-extern const struct frame_unwind dummy_frame_unwind;
+extern const struct frame_unwind_legacy dummy_frame_unwind;
 
 /* Destructor for dummy_frame.  DATA is supplied by registrant.
    REGISTERS_VALID is 1 for dummy_frame_pop, 0 for dummy_frame_discard.  */
diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
index 50efd4eb5ff..54813d00b03 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -468,8 +468,7 @@  tailcall_frame_prev_arch (const frame_info_ptr &this_frame,
 /* Virtual tail call frame unwinder if dwarf2_tailcall_sniffer_first finds
    a chain to create.  */
 
-const struct frame_unwind dwarf2_tailcall_frame_unwind =
-{
+const struct frame_unwind_legacy dwarf2_tailcall_frame_unwind (
   "dwarf2 tailcall",
   TAILCALL_FRAME,
   FRAME_UNWIND_DEBUGINFO,
@@ -480,7 +479,7 @@  const struct frame_unwind dwarf2_tailcall_frame_unwind =
   tailcall_frame_sniffer,
   tailcall_frame_dealloc_cache,
   tailcall_frame_prev_arch
-};
+);
 
 void _initialize_tailcall_frame ();
 void
diff --git a/gdb/dwarf2/frame-tailcall.h b/gdb/dwarf2/frame-tailcall.h
index 3f49487ac2a..3569c7dd3e2 100644
--- a/gdb/dwarf2/frame-tailcall.h
+++ b/gdb/dwarf2/frame-tailcall.h
@@ -34,6 +34,6 @@  extern struct value *
   dwarf2_tailcall_prev_register_first (const frame_info_ptr &this_frame,
 				       void **tailcall_cachep, int regnum);
 
-extern const struct frame_unwind dwarf2_tailcall_frame_unwind;
+extern const struct frame_unwind_legacy dwarf2_tailcall_frame_unwind;
 
 #endif /* !DWARF2_FRAME_TAILCALL_H */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index c51e8c16147..3d66ff3d935 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1329,16 +1329,15 @@  dwarf2_frame_sniffer (const struct frame_unwind *self,
   if (fde->cie->signal_frame
       || dwarf2_frame_signal_frame_p (get_frame_arch (this_frame),
 				      this_frame))
-    return self->type == SIGTRAMP_FRAME;
+    return self->type () == SIGTRAMP_FRAME;
 
-  if (self->type != NORMAL_FRAME)
+  if (self->type () != NORMAL_FRAME)
     return 0;
 
   return 1;
 }
 
-static const struct frame_unwind dwarf2_frame_unwind =
-{
+static const struct frame_unwind_legacy dwarf2_frame_unwind (
   "dwarf2",
   NORMAL_FRAME,
   FRAME_UNWIND_DEBUGINFO,
@@ -1348,10 +1347,9 @@  static const struct frame_unwind dwarf2_frame_unwind =
   NULL,
   dwarf2_frame_sniffer,
   dwarf2_frame_dealloc_cache
-};
+);
 
-static const struct frame_unwind dwarf2_signal_frame_unwind =
-{
+static const struct frame_unwind_legacy dwarf2_signal_frame_unwind (
   "dwarf2 signal",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_DEBUGINFO,
@@ -1363,7 +1361,7 @@  static const struct frame_unwind dwarf2_signal_frame_unwind =
 
   /* TAILCALL_CACHE can never be in such frame to need dealloc_cache.  */
   NULL
-};
+);
 
 /* Append the DWARF-2 frame unwinders to GDBARCH's list.  */
 
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index afc1258c6a9..7be01ec2203 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -126,7 +126,7 @@  frame_unwind_try_unwinder (const frame_info_ptr &this_frame, void **this_cache,
 
   try
     {
-      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name);
+      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name ());
       res = unwinder->sniffer (unwinder, this_frame, this_cache);
     }
   catch (const gdb_exception &ex)
@@ -332,6 +332,59 @@  frame_unwind_got_address (const frame_info_ptr &frame, int regnum,
   return reg_val;
 }
 
+/* This method just passes the parameters to the callback pointer.  */
+enum unwind_stop_reason
+frame_unwind_legacy::stop_reason (const frame_info_ptr &this_frame,
+				  void **this_prologue_cache) const
+{
+  return stop_reason_p (this_frame, this_prologue_cache);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+void
+frame_unwind_legacy::this_id (const frame_info_ptr &this_frame,
+			      void **this_prologue_cache,
+			      struct frame_id *id) const
+{
+  return this_id_p (this_frame, this_prologue_cache, id);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+struct value *
+frame_unwind_legacy::prev_register (const frame_info_ptr &this_frame,
+				    void **this_prologue_cache,
+				    int regnum) const
+{
+  return prev_register_p (this_frame, this_prologue_cache, regnum);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+int
+frame_unwind_legacy::sniffer (const struct frame_unwind *self,
+			      const frame_info_ptr &this_frame,
+			      void **this_prologue_cache) const
+{
+  return sniffer_p (self, this_frame, this_prologue_cache);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+void
+frame_unwind_legacy::dealloc_cache (frame_info *self, void *this_cache) const
+{
+  if (dealloc_cache_p != nullptr)
+    dealloc_cache_p (self, this_cache);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+struct gdbarch *
+frame_unwind_legacy::prev_arch (const frame_info_ptr &this_frame,
+				void **this_prologue_cache) const
+{
+  if (prev_arch_p == nullptr)
+    error (_("No prev_arch callback installed"));
+  return prev_arch_p (this_frame, this_prologue_cache);
+}
+
 /* Implement "maintenance info frame-unwinders" command.  */
 
 static void
@@ -349,9 +402,9 @@  maintenance_info_frame_unwinders (const char *args, int from_tty)
 
   for (auto unwinder : table)
     {
-      const char *name = unwinder->name;
-      const char *type = frame_type_str (unwinder->type);
-      const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class);
+      const char *name = unwinder->name ();
+      const char *type = frame_type_str (unwinder->type ());
+      const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class ());
 
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", name);
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index deab4f7dbfb..b078945a200 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -169,25 +169,148 @@  enum frame_unwind_class
   FRAME_UNWIND_ARCH,
 };
 
-struct frame_unwind
+class frame_unwind
 {
-  const char *name;
+  const char *m_name;
   /* The frame's type.  Should this instead be a collection of
      predicates that test the frame for various attributes?  */
-  enum frame_type type;
+  enum frame_type m_type;
   /* What kind of unwinder is this.  It generally follows from where
      the unwinder was added or where it looks for information to do the
      unwinding.  */
-  enum frame_unwind_class unwinder_class;
+  enum frame_unwind_class m_unwinder_class;
+  const struct frame_data *m_unwind_data;
+public:
+  frame_unwind (const char *name, frame_type type, frame_unwind_class uclass,
+		       const struct frame_data *data)
+    : m_name (name), m_type (type), m_unwinder_class (uclass),
+      m_unwind_data (data)
+    { }
+
+  const char *name () const
+  {
+    return m_name;
+  }
+
+  enum frame_type type () const
+  {
+    return m_type;
+  }
+
+  enum frame_unwind_class unwinder_class () const
+  {
+    return m_unwinder_class;
+  }
+
+  const struct frame_data *unwind_data () const
+  {
+    return m_unwind_data;
+  }
+
+  /* Default stop_reason function.  It reports NO_REASON, unless the
+     frame is the outermost.  */
+  virtual enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
+					       void **this_prologue_cache) const
+  {
+    return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
+  }
+
+  /* Default frame sniffer.  Will always return that the unwinder
+     is able to unwind the frame.  */
+  virtual int sniffer (const frame_unwind *self,
+		       const frame_info_ptr &this_frame,
+		       void **this_prologue_cache) const
+  {
+    return 1;
+  }
+
+  /* Calculate the ID of the given frame using this unwinder.  */
+  virtual void this_id (const frame_info_ptr &this_frame,
+			void **this_prologue_cache,
+			struct frame_id *id) const = 0;
+
+  /* Get the value of a register in a previous frame.  */
+  virtual struct value *prev_register (const frame_info_ptr &this_frame,
+				       void **this_prologue_cache,
+				       int regnum) const = 0;
+
+  /* The following methods are here mostly for interface functionality.  They
+     all throw an error when called, as a safe way to check if an unwinder has
+     implemented the desired functionality.  */
+
+  /* Properly deallocate the cache.  */
+  virtual void dealloc_cache (frame_info *self, void *this_cache) const
+  {
+    internal_error (_("No method dealloc_cache implemented for unwinder %s"),
+		    m_name);
+  }
+
+  /* Get the previous architecture.  */
+  virtual struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+				     void **this_prologue_cache) const
+  {
+    error (_("No method prev_arch implemented for unwinder %s"), m_name);
+  }
+};
+
+/* This is a legacy version of the frame unwinder.  The original struct
+   used function pointers for callbacks, this updated version has a
+   method that just passes the parameters along to the callback
+   pointer.
+   Do not use this class for new unwinders.  Instead, see other classes
+   that inherit from frame_unwind, such as the python unwinder.  */
+class frame_unwind_legacy : public frame_unwind
+{
+private:
   /* Should an attribute indicating the frame's address-in-block go
      here?  */
-  frame_unwind_stop_reason_ftype *stop_reason;
-  frame_this_id_ftype *this_id;
-  frame_prev_register_ftype *prev_register;
-  const struct frame_data *unwind_data;
-  frame_sniffer_ftype *sniffer;
-  frame_dealloc_cache_ftype *dealloc_cache;
-  frame_prev_arch_ftype *prev_arch;
+  frame_unwind_stop_reason_ftype *stop_reason_p;
+  frame_this_id_ftype *this_id_p;
+  frame_prev_register_ftype *prev_register_p;
+  frame_sniffer_ftype *sniffer_p;
+  frame_dealloc_cache_ftype *dealloc_cache_p;
+  frame_prev_arch_ftype *prev_arch_p;
+public:
+  frame_unwind_legacy (const char *n, frame_type t, frame_unwind_class c,
+		       frame_unwind_stop_reason_ftype *sr,
+		       frame_this_id_ftype *ti,
+		       frame_prev_register_ftype *pr,
+		       const struct frame_data *ud,
+		       frame_sniffer_ftype *s,
+		       frame_dealloc_cache_ftype *dc = nullptr,
+		       frame_prev_arch_ftype *pa = nullptr)
+  : frame_unwind (n, t, c, ud), stop_reason_p (sr),
+    this_id_p (ti), prev_register_p (pr), sniffer_p (s),
+    dealloc_cache_p (dc), prev_arch_p (pa) { }
+
+  /* This method just passes the parameters to the callback pointer.  */
+  enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
+					void **this_prologue_cache) const override;
+
+  /* This method just passes the parameters to the callback pointer.  */
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		 struct frame_id *id) const override;
+
+  /* This method just passes the parameters to the callback pointer.  */
+  struct value *prev_register (const frame_info_ptr &this_frame,
+				void **this_prologue_cache,
+				int regnum) const override;
+
+  /* This method just passes the parameters to the callback pointer.  */
+  int sniffer (const frame_unwind *self,
+		const frame_info_ptr &this_frame,
+		void **this_prologue_cache) const override;
+
+  /* This method just passes the parameters to the callback pointer.
+     It is safe to call this method if no callback is installed, it
+     just turns into a noop.  */
+  void dealloc_cache (frame_info *self, void *this_cache) const override;
+
+  /* This method just passes the parameters to the callback pointer.
+     If this function is called with no installed callback, this method
+     will error out.  Wrap calls in try-catch blocks.  */
+  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			      void **this_prologue_cache) const override;
 };
 
 /* Register a frame unwinder, _prepending_ it to the front of the
diff --git a/gdb/frame.c b/gdb/frame.c
index a6900b28072..3d6bf746c39 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -269,12 +269,10 @@  frame_addr_hash_eq (const void *a, const void *b)
 static void
 frame_info_del (frame_info *frame)
 {
-  if (frame->prologue_cache != nullptr
-      && frame->unwind->dealloc_cache != nullptr)
+  if (frame->prologue_cache != nullptr)
     frame->unwind->dealloc_cache (frame, frame->prologue_cache);
 
-  if (frame->base_cache != nullptr
-      && frame->base->unwind->dealloc_cache != nullptr)
+  if (frame->base_cache != nullptr)
     frame->base->unwind->dealloc_cache (frame, frame->base_cache);
 }
 
@@ -487,12 +485,12 @@  frame_info::to_string () const
   res += string_printf ("{level=%d,", fi->level);
 
   if (fi->unwind != NULL)
-    res += string_printf ("type=%s,", frame_type_str (fi->unwind->type));
+    res += string_printf ("type=%s,", frame_type_str (fi->unwind->type ()));
   else
     res += "type=<unknown>,";
 
   if (fi->unwind != NULL)
-    res += string_printf ("unwinder=\"%s\",", fi->unwind->name);
+    res += string_printf ("unwinder=\"%s\",", fi->unwind->name ());
   else
     res += "unwinder=<unknown>,";
 
@@ -2363,7 +2361,7 @@  get_prev_frame_always_1 (const frame_info_ptr &this_frame)
      This check is valid only if this frame and the next frame are NORMAL.
      See the comment at frame_id_inner for details.  */
   if (get_frame_type (this_frame) == NORMAL_FRAME
-      && this_frame->next->unwind->type == NORMAL_FRAME
+      && this_frame->next->unwind->type () == NORMAL_FRAME
       && frame_id_inner (get_frame_arch (frame_info_ptr (this_frame->next)),
 			 get_frame_id (this_frame),
 			 get_frame_id (frame_info_ptr (this_frame->next))))
@@ -2991,7 +2989,7 @@  get_frame_type (const frame_info_ptr &frame)
     /* Initialize the frame's unwinder because that's what
        provides the frame's type.  */
     frame_unwind_find_by_frame (frame, &frame->prologue_cache);
-  return frame->unwind->type;
+  return frame->unwind->type ();
 }
 
 struct program_space *
@@ -3072,11 +3070,15 @@  frame_unwind_arch (const frame_info_ptr &next_frame)
       if (next_frame->unwind == NULL)
 	frame_unwind_find_by_frame (next_frame, &next_frame->prologue_cache);
 
-      if (next_frame->unwind->prev_arch != NULL)
-	arch = next_frame->unwind->prev_arch (next_frame,
-					      &next_frame->prologue_cache);
-      else
-	arch = get_frame_arch (next_frame);
+      try
+	{
+	  arch = next_frame->unwind->prev_arch (next_frame,
+						&next_frame->prologue_cache);
+	}
+      catch (gdb_exception &e)
+	{
+	  arch = get_frame_arch (next_frame);
+	}
 
       next_frame->prev_arch.arch = arch;
       next_frame->prev_arch.p = true;
diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c
index 7a7c4904475..4941e4a0741 100644
--- a/gdb/frv-linux-tdep.c
+++ b/gdb/frv-linux-tdep.c
@@ -332,8 +332,7 @@  frv_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy frv_linux_sigtramp_frame_unwind (
   "frv linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -342,7 +341,7 @@  static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
   frv_linux_sigtramp_frame_prev_register,
   NULL,
   frv_linux_sigtramp_frame_sniffer
-};
+);
 
 /* The FRV kernel defines ELF_NGREG as 46.  We add 2 in order to include
    the loadmap addresses in the register set.  (See below for more info.)  */
diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c
index a1845a25750..b022cc75f85 100644
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1404,7 +1404,7 @@  frv_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind frv_frame_unwind = {
+static const struct frame_unwind_legacy frv_frame_unwind (
   "frv prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1413,7 +1413,7 @@  static const struct frame_unwind frv_frame_unwind = {
   frv_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 frv_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
index e3827ad96e7..2ef77a271b4 100644
--- a/gdb/ft32-tdep.c
+++ b/gdb/ft32-tdep.c
@@ -525,8 +525,7 @@  ft32_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind ft32_frame_unwind =
-{
+static const struct frame_unwind_legacy ft32_frame_unwind (
   "ft32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -535,7 +534,7 @@  static const struct frame_unwind ft32_frame_unwind =
   ft32_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Return the base address of this_frame.  */
 
diff --git a/gdb/h8300-tdep.c b/gdb/h8300-tdep.c
index 5b4466ed316..a845900d299 100644
--- a/gdb/h8300-tdep.c
+++ b/gdb/h8300-tdep.c
@@ -500,7 +500,7 @@  h8300_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind h8300_frame_unwind = {
+static const struct frame_unwind_legacy h8300_frame_unwind (
   "h8300 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -509,7 +509,7 @@  static const struct frame_unwind h8300_frame_unwind = {
   h8300_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 h8300_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c
index 2619b60655a..f43a5e73856 100644
--- a/gdb/hppa-linux-tdep.c
+++ b/gdb/hppa-linux-tdep.c
@@ -308,7 +308,7 @@  hppa_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy hppa_linux_sigtramp_frame_unwind (
   "hppa linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -317,7 +317,7 @@  static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = {
   hppa_linux_sigtramp_frame_prev_register,
   NULL,
   hppa_linux_sigtramp_frame_sniffer
-};
+);
 
 /* Attempt to find (and return) the global pointer for the given
    function.
diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c
index 2447b87ebae..1fb68f261c6 100644
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -2282,8 +2282,7 @@  hppa_frame_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind hppa_frame_unwind =
-{
+static const struct frame_unwind_legacy hppa_frame_unwind (
   "hppa unwind table",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2292,7 +2291,7 @@  static const struct frame_unwind hppa_frame_unwind =
   hppa_frame_prev_register,
   NULL,
   hppa_frame_unwind_sniffer
-};
+);
 
 /* This is a generic fallback frame unwinder that kicks in if we fail all
    the other ones.  Normally we would expect the stub and regular unwinder
@@ -2396,8 +2395,7 @@  hppa_fallback_frame_prev_register (const frame_info_ptr &this_frame,
 					  info->saved_regs, regnum);
 }
 
-static const struct frame_unwind hppa_fallback_frame_unwind =
-{
+static const struct frame_unwind_legacy hppa_fallback_frame_unwind (
   "hppa prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2406,7 +2404,7 @@  static const struct frame_unwind hppa_fallback_frame_unwind =
   hppa_fallback_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Stub frames, used for all kinds of call stubs.  */
 struct hppa_stub_unwind_cache
@@ -2479,7 +2477,7 @@  hppa_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind hppa_stub_frame_unwind = {
+static const struct frame_unwind_legacy hppa_stub_frame_unwind (
   "hppa stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2488,7 +2486,7 @@  static const struct frame_unwind hppa_stub_frame_unwind = {
   hppa_stub_frame_prev_register,
   NULL,
   hppa_stub_unwind_sniffer
-};
+);
 
 CORE_ADDR
 hppa_unwind_pc (struct gdbarch *gdbarch, const frame_info_ptr &next_frame)
diff --git a/gdb/i386-obsd-tdep.c b/gdb/i386-obsd-tdep.c
index 1d2b1a09d16..ea8addce599 100644
--- a/gdb/i386-obsd-tdep.c
+++ b/gdb/i386-obsd-tdep.c
@@ -390,7 +390,7 @@  i386obsd_trapframe_sniffer (const struct frame_unwind *self,
 		   || startswith (name, "Xsoft")));
 }
 
-static const struct frame_unwind i386obsd_trapframe_unwind = {
+static const struct frame_unwind_legacy i386obsd_trapframe_unwind (
   "i386 openbsd trap",
   /* FIXME: kettenis/20051219: This really is more like an interrupt
      frame, but SIGTRAMP_FRAME would print <signal handler called>,
@@ -402,7 +402,7 @@  static const struct frame_unwind i386obsd_trapframe_unwind = {
   i386obsd_trapframe_prev_register,
   NULL,
   i386obsd_trapframe_sniffer
-};
+);
 
 
 static void 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index b554e9ad9b0..5e7f0dc15a7 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2139,8 +2139,7 @@  i386_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind i386_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_frame_unwind (
   "i386 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2149,7 +2148,7 @@  static const struct frame_unwind i386_frame_unwind =
   i386_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Normal frames, but in a function epilogue.  */
 
@@ -2295,8 +2294,7 @@  i386_epilogue_frame_prev_register (const frame_info_ptr &this_frame,
   return i386_frame_prev_register (this_frame, this_cache, regnum);
 }
 
-static const struct frame_unwind i386_epilogue_override_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_epilogue_override_frame_unwind (
   "i386 epilogue override",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2305,10 +2303,9 @@  static const struct frame_unwind i386_epilogue_override_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_epilogue_override_frame_sniffer
-};
+);
 
-static const struct frame_unwind i386_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_epilogue_frame_unwind (
   "i386 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2317,7 +2314,7 @@  static const struct frame_unwind i386_epilogue_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_epilogue_frame_sniffer
-};
+);
 
 
 /* Stack-based trampolines.  */
@@ -2390,8 +2387,7 @@  i386_stack_tramp_frame_sniffer (const struct frame_unwind *self,
     return 0;
 }
 
-static const struct frame_unwind i386_stack_tramp_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_stack_tramp_frame_unwind (
   "i386 stack tramp",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2400,7 +2396,7 @@  static const struct frame_unwind i386_stack_tramp_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_stack_tramp_frame_sniffer
-};
+);
 
 /* Generate a bytecode expression to get the value of the saved PC.  */
 
@@ -2540,8 +2536,7 @@  i386_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind i386_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_sigtramp_frame_unwind (
   "i386 sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2550,7 +2545,7 @@  static const struct frame_unwind i386_sigtramp_frame_unwind =
   i386_sigtramp_frame_prev_register,
   NULL,
   i386_sigtramp_frame_sniffer
-};
+);
 
 
 static CORE_ADDR
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index b10cc251bf1..11375d5bc69 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -2162,8 +2162,7 @@  ia64_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
     }
 }
  
-static const struct frame_unwind ia64_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_frame_unwind (
   "ia64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2172,7 +2171,7 @@  static const struct frame_unwind ia64_frame_unwind =
   &ia64_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Signal trampolines.  */
 
@@ -2352,8 +2351,7 @@  ia64_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind ia64_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_sigtramp_frame_unwind (
   "ia64 sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2362,7 +2360,7 @@  static const struct frame_unwind ia64_sigtramp_frame_unwind =
   ia64_sigtramp_frame_prev_register,
   NULL,
   ia64_sigtramp_frame_sniffer
-};
+);
 
 
 
@@ -3013,8 +3011,7 @@  ia64_libunwind_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind ia64_libunwind_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_libunwind_frame_unwind (
   "ia64 libunwind",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3024,7 +3021,7 @@  static const struct frame_unwind ia64_libunwind_frame_unwind =
   NULL,
   ia64_libunwind_frame_sniffer,
   libunwind_frame_dealloc_cache
-};
+);
 
 static void
 ia64_libunwind_sigtramp_frame_this_id (const frame_info_ptr &this_frame,
@@ -3103,8 +3100,7 @@  ia64_libunwind_sigtramp_frame_sniffer (const struct frame_unwind *self,
     return ia64_sigtramp_frame_sniffer (self, this_frame, this_cache);
 }
 
-static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_libunwind_sigtramp_frame_unwind (
   "ia64 libunwind sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3113,7 +3109,7 @@  static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind =
   ia64_libunwind_sigtramp_frame_prev_register,
   NULL,
   ia64_libunwind_sigtramp_frame_sniffer
-};
+);
 
 /* Set of libunwind callback acccessor functions.  */
 unw_accessors_t ia64_unw_accessors =
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index ec80fabe611..d75157fb68c 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -269,7 +269,7 @@  inline_frame_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
-const struct frame_unwind inline_frame_unwind = {
+const struct frame_unwind_legacy inline_frame_unwind (
   "inline",
   INLINE_FRAME,
   FRAME_UNWIND_GDB,
@@ -278,7 +278,7 @@  const struct frame_unwind inline_frame_unwind = {
   inline_frame_prev_register,
   NULL,
   inline_frame_sniffer
-};
+);
 
 /* Return non-zero if BLOCK, an inlined function block containing PC,
    has a group of contiguous instructions starting at PC (but not
diff --git a/gdb/inline-frame.h b/gdb/inline-frame.h
index 3ddc9cf2335..96412154f6a 100644
--- a/gdb/inline-frame.h
+++ b/gdb/inline-frame.h
@@ -27,7 +27,7 @@  struct process_stratum_target;
 
 /* The inline frame unwinder.  */
 
-extern const struct frame_unwind inline_frame_unwind;
+extern const struct frame_unwind_legacy inline_frame_unwind;
 
 /* Skip all inlined functions whose call sites are at the current PC.
 
diff --git a/gdb/iq2000-tdep.c b/gdb/iq2000-tdep.c
index e0db1b0de4e..094f627e37b 100644
--- a/gdb/iq2000-tdep.c
+++ b/gdb/iq2000-tdep.c
@@ -424,7 +424,7 @@  iq2000_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
   *this_id = frame_id_build (cache->saved_sp, cache->pc);
 }
 
-static const struct frame_unwind iq2000_frame_unwind = {
+static const struct frame_unwind_legacy iq2000_frame_unwind (
   "iq2000 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -433,7 +433,7 @@  static const struct frame_unwind iq2000_frame_unwind = {
   iq2000_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 iq2000_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/jit.c b/gdb/jit.c
index 63c496ba0b5..804dcbbbcb5 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1105,8 +1105,7 @@  jit_frame_prev_register (const frame_info_ptr &this_frame, void **cache, int reg
 /* Relay everything back to the unwinder registered by the JIT debug
    info reader.*/
 
-static const struct frame_unwind jit_frame_unwind =
-{
+static const struct frame_unwind_legacy jit_frame_unwind (
   "jit",
   NORMAL_FRAME,
   FRAME_UNWIND_EXTENSION,
@@ -1116,7 +1115,7 @@  static const struct frame_unwind jit_frame_unwind =
   NULL,
   jit_frame_sniffer,
   jit_dealloc_cache
-};
+);
 
 
 /* This is the information that is stored at jit_gdbarch_data for each
diff --git a/gdb/lm32-tdep.c b/gdb/lm32-tdep.c
index 4eb5f2ad426..c94aef4d839 100644
--- a/gdb/lm32-tdep.c
+++ b/gdb/lm32-tdep.c
@@ -447,7 +447,7 @@  lm32_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind lm32_frame_unwind = {
+static const struct frame_unwind_legacy lm32_frame_unwind (
   "lm32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -456,7 +456,7 @@  static const struct frame_unwind lm32_frame_unwind = {
   lm32_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 lm32_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index 6bb6d31c7a6..6e5a34e19ba 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -450,7 +450,7 @@  loongarch_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_register (info, this_frame, regnum);
 }
 
-static const struct frame_unwind loongarch_frame_unwind = {
+static const struct frame_unwind_legacy loongarch_frame_unwind (
   "loongarch prologue",
   /*.type	   =*/NORMAL_FRAME,
   /*.unwinder_class=*/FRAME_UNWIND_ARCH,
@@ -460,8 +460,8 @@  static const struct frame_unwind loongarch_frame_unwind = {
   /*.unwind_data   =*/nullptr,
   /*.sniffer	   =*/default_frame_sniffer,
   /*.dealloc_cache =*/nullptr,
-  /*.prev_arch	   =*/nullptr,
-};
+  /*.prev_arch	   =*/nullptr
+);
 
 /* Write the contents of buffer VAL into the general-purpose argument
    register defined by GAR in REGCACHE.  GAR indicates the available
diff --git a/gdb/m32c-tdep.c b/gdb/m32c-tdep.c
index bce12c5fae7..376b3622840 100644
--- a/gdb/m32c-tdep.c
+++ b/gdb/m32c-tdep.c
@@ -1955,7 +1955,7 @@  m32c_prev_register (const frame_info_ptr &this_frame,
 }
 
 
-static const struct frame_unwind m32c_unwind = {
+static const struct frame_unwind_legacy m32c_unwind (
   "m32c prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1964,7 +1964,7 @@  static const struct frame_unwind m32c_unwind = {
   m32c_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 
 /* Inferior calls.  */
diff --git a/gdb/m32r-linux-tdep.c b/gdb/m32r-linux-tdep.c
index 11a150a92b8..72c18337c4a 100644
--- a/gdb/m32r-linux-tdep.c
+++ b/gdb/m32r-linux-tdep.c
@@ -301,7 +301,7 @@  m32r_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind m32r_linux_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy m32r_linux_sigtramp_frame_unwind (
   "m32r linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -310,7 +310,7 @@  static const struct frame_unwind m32r_linux_sigtramp_frame_unwind = {
   m32r_linux_sigtramp_frame_prev_register,
   NULL,
   m32r_linux_sigtramp_frame_sniffer
-};
+);
 
 /* Mapping between the registers in `struct pt_regs'
    format and GDB's register array layout.  */
diff --git a/gdb/m32r-tdep.c b/gdb/m32r-tdep.c
index aadb8c00d86..8300528364e 100644
--- a/gdb/m32r-tdep.c
+++ b/gdb/m32r-tdep.c
@@ -831,7 +831,7 @@  m32r_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind m32r_frame_unwind = {
+static const struct frame_unwind_legacy m32r_frame_unwind (
   "m32r prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -840,7 +840,7 @@  static const struct frame_unwind m32r_frame_unwind = {
   m32r_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 m32r_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c
index e14ac622fda..84a44f525c2 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -933,7 +933,7 @@  m68hc11_frame_prev_register (const frame_info_ptr &this_frame,
   return value;
 }
 
-static const struct frame_unwind m68hc11_frame_unwind = {
+static const struct frame_unwind_legacy m68hc11_frame_unwind (
   "m68hc11 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -942,7 +942,7 @@  static const struct frame_unwind m68hc11_frame_unwind = {
   m68hc11_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 m68hc11_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/m68k-linux-tdep.c b/gdb/m68k-linux-tdep.c
index 7cca4f86394..f7388d952e9 100644
--- a/gdb/m68k-linux-tdep.c
+++ b/gdb/m68k-linux-tdep.c
@@ -314,8 +314,7 @@  m68k_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return m68k_linux_pc_in_sigtramp (this_frame);
 }
 
-static const struct frame_unwind m68k_linux_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy m68k_linux_sigtramp_frame_unwind (
   "m68k linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -324,7 +323,7 @@  static const struct frame_unwind m68k_linux_sigtramp_frame_unwind =
   m68k_linux_sigtramp_frame_prev_register,
   NULL,
   m68k_linux_sigtramp_frame_sniffer
-};
+);
 
 /* Register maps for supply/collect regset functions.  */
 
diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c
index 2ff507e7816..4318484a5c5 100644
--- a/gdb/m68k-tdep.c
+++ b/gdb/m68k-tdep.c
@@ -1007,8 +1007,7 @@  m68k_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind m68k_frame_unwind =
-{
+static const struct frame_unwind_legacy m68k_frame_unwind (
   "m68k prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1017,7 +1016,7 @@  static const struct frame_unwind m68k_frame_unwind =
   m68k_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 m68k_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/mep-tdep.c b/gdb/mep-tdep.c
index 7dd6371f995..9b6f491c840 100644
--- a/gdb/mep-tdep.c
+++ b/gdb/mep-tdep.c
@@ -2061,7 +2061,7 @@  mep_frame_prev_register (const frame_info_ptr &this_frame,
 }
 
 
-static const struct frame_unwind mep_frame_unwind = {
+static const struct frame_unwind_legacy mep_frame_unwind (
   "mep prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2070,7 +2070,7 @@  static const struct frame_unwind mep_frame_unwind = {
   mep_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 
 /* Return values.  */
diff --git a/gdb/microblaze-tdep.c b/gdb/microblaze-tdep.c
index 94e8b0cee53..f2df0743c8c 100644
--- a/gdb/microblaze-tdep.c
+++ b/gdb/microblaze-tdep.c
@@ -478,8 +478,7 @@  microblaze_frame_prev_register (const frame_info_ptr &this_frame,
 
 }
 
-static const struct frame_unwind microblaze_frame_unwind =
-{
+static const struct frame_unwind_legacy microblaze_frame_unwind (
   "microblaze prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -488,7 +487,7 @@  static const struct frame_unwind microblaze_frame_unwind =
   microblaze_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 microblaze_frame_base_address (const frame_info_ptr &next_frame,
diff --git a/gdb/mips-sde-tdep.c b/gdb/mips-sde-tdep.c
index 18cb9485639..783e6fd35ab 100644
--- a/gdb/mips-sde-tdep.c
+++ b/gdb/mips-sde-tdep.c
@@ -160,8 +160,7 @@  mips_sde_frame_sniffer (const struct frame_unwind *self,
 
 /* Data structure for the SDE frame unwinder.  */
 
-static const struct frame_unwind mips_sde_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_sde_frame_unwind (
   "mips sde sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -170,7 +169,7 @@  static const struct frame_unwind mips_sde_frame_unwind =
   mips_sde_frame_prev_register,
   NULL,
   mips_sde_frame_sniffer
-};
+);
 
 /* Implement the this_base, this_locals, and this_args hooks
    for the normal unwinder.  */
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index c51eeecdfef..0b6c90212f7 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -2925,8 +2925,7 @@  mips_insn16_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_insn16_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_insn16_frame_unwind (
   "mips insn16 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2935,7 +2934,7 @@  static const struct frame_unwind mips_insn16_frame_unwind =
   mips_insn16_frame_prev_register,
   NULL,
   mips_insn16_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_insn16_frame_base_address (const frame_info_ptr &this_frame,
@@ -3362,8 +3361,7 @@  mips_micro_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_micro_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_micro_frame_unwind (
   "mips micro prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3372,7 +3370,7 @@  static const struct frame_unwind mips_micro_frame_unwind =
   mips_micro_frame_prev_register,
   NULL,
   mips_micro_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_micro_frame_base_address (const frame_info_ptr &this_frame,
@@ -3742,8 +3740,7 @@  mips_insn32_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_insn32_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_insn32_frame_unwind (
   "mips insn32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3752,7 +3749,7 @@  static const struct frame_unwind mips_insn32_frame_unwind =
   mips_insn32_frame_prev_register,
   NULL,
   mips_insn32_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_insn32_frame_base_address (const frame_info_ptr &this_frame,
@@ -3859,8 +3856,7 @@  mips_stub_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_stub_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_stub_frame_unwind (
   "mips stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3869,7 +3865,7 @@  static const struct frame_unwind mips_stub_frame_unwind =
   mips_stub_frame_prev_register,
   NULL,
   mips_stub_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_stub_frame_base_address (const frame_info_ptr &this_frame,
diff --git a/gdb/mn10300-tdep.c b/gdb/mn10300-tdep.c
index 565300afd3c..f989bd6c75b 100644
--- a/gdb/mn10300-tdep.c
+++ b/gdb/mn10300-tdep.c
@@ -1127,7 +1127,7 @@  mn10300_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind mn10300_frame_unwind = {
+static const struct frame_unwind_legacy mn10300_frame_unwind (
   "mn10300 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1136,7 +1136,7 @@  static const struct frame_unwind mn10300_frame_unwind = {
   mn10300_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static void
 mn10300_frame_unwind_init (struct gdbarch *gdbarch)
diff --git a/gdb/moxie-tdep.c b/gdb/moxie-tdep.c
index 82d3478ed68..f69810f83c3 100644
--- a/gdb/moxie-tdep.c
+++ b/gdb/moxie-tdep.c
@@ -585,7 +585,7 @@  moxie_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind moxie_frame_unwind = {
+static const struct frame_unwind_legacy moxie_frame_unwind (
   "moxie prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -594,7 +594,7 @@  static const struct frame_unwind moxie_frame_unwind = {
   moxie_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Return the base address of this_frame.  */
 
diff --git a/gdb/msp430-tdep.c b/gdb/msp430-tdep.c
index 6d7fee230c1..17ebddf386a 100644
--- a/gdb/msp430-tdep.c
+++ b/gdb/msp430-tdep.c
@@ -542,7 +542,7 @@  msp430_prev_register (const frame_info_ptr &this_frame,
     return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind msp430_unwind = {
+static const struct frame_unwind_legacy msp430_unwind (
   "msp430 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -551,7 +551,7 @@  static const struct frame_unwind msp430_unwind = {
   msp430_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Implement the "dwarf2_reg_to_regnum" gdbarch method.  */
 
diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
index 910a6f7a1d0..e08ead24b4f 100644
--- a/gdb/nds32-tdep.c
+++ b/gdb/nds32-tdep.c
@@ -988,8 +988,7 @@  nds32_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind nds32_frame_unwind =
-{
+static const struct frame_unwind_legacy nds32_frame_unwind (
   "nds32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -997,8 +996,8 @@  static const struct frame_unwind nds32_frame_unwind =
   nds32_frame_this_id,
   nds32_frame_prev_register,
   NULL,
-  default_frame_sniffer,
-};
+  default_frame_sniffer
+);
 
 /* Return the frame base address of *THIS_FRAME.  */
 
@@ -1373,8 +1372,7 @@  nds32_epilogue_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind nds32_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy nds32_epilogue_frame_unwind (
   "nds32 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1383,7 +1381,7 @@  static const struct frame_unwind nds32_epilogue_frame_unwind =
   nds32_epilogue_frame_prev_register,
   NULL,
   nds32_epilogue_frame_sniffer
-};
+);
 
 
 /* Floating type and struct type that has only one floating type member
diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
index 5e68764bd6a..7bb0d5a973f 100644
--- a/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -1977,8 +1977,7 @@  nios2_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
 /* Data structures for the normal prologue-analysis-based
    unwinder.  */
 
-static const struct frame_unwind nios2_frame_unwind =
-{
+static const struct frame_unwind_legacy nios2_frame_unwind (
   "nios2 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1987,7 +1986,7 @@  static const struct frame_unwind nios2_frame_unwind =
   nios2_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static const struct frame_base nios2_frame_base =
 {
@@ -2079,8 +2078,7 @@  nios2_stub_frame_sniffer (const struct frame_unwind *self,
 
 /* Define the data structures for the stub unwinder.  */
 
-static const struct frame_unwind nios2_stub_frame_unwind =
-{
+static const struct frame_unwind_legacy nios2_stub_frame_unwind (
   "nios2 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2089,7 +2087,7 @@  static const struct frame_unwind nios2_stub_frame_unwind =
   nios2_stub_frame_prev_register,
   NULL,
   nios2_stub_frame_sniffer
-};
+);
 
 
 
diff --git a/gdb/or1k-tdep.c b/gdb/or1k-tdep.c
index 6e0466c3408..bdfad9bfb03 100644
--- a/gdb/or1k-tdep.c
+++ b/gdb/or1k-tdep.c
@@ -1125,7 +1125,7 @@  or1k_frame_prev_register (const frame_info_ptr &this_frame,
 
 /* Data structures for the normal prologue-analysis-based unwinder.  */
 
-static const struct frame_unwind or1k_frame_unwind = {
+static const struct frame_unwind_legacy or1k_frame_unwind (
   "or1k prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1134,8 +1134,8 @@  static const struct frame_unwind or1k_frame_unwind = {
   or1k_frame_prev_register,
   NULL,
   default_frame_sniffer,
-  NULL,
-};
+  NULL
+);
 
 /* Architecture initialization for OpenRISC 1000.  */
 
diff --git a/gdb/ppc-fbsd-tdep.c b/gdb/ppc-fbsd-tdep.c
index 12eb396a0e3..0403b5662b8 100644
--- a/gdb/ppc-fbsd-tdep.c
+++ b/gdb/ppc-fbsd-tdep.c
@@ -262,7 +262,7 @@  ppcfbsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_register (cache, this_frame, regnum);
 }
 
-static const struct frame_unwind ppcfbsd_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy ppcfbsd_sigtramp_frame_unwind (
   "ppc freebsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -271,7 +271,7 @@  static const struct frame_unwind ppcfbsd_sigtramp_frame_unwind = {
   ppcfbsd_sigtramp_frame_prev_register,
   NULL,
   ppcfbsd_sigtramp_frame_sniffer
-};
+);
 
 static enum return_value_convention
 ppcfbsd_return_value (struct gdbarch *gdbarch, struct value *function,
diff --git a/gdb/ppc-obsd-tdep.c b/gdb/ppc-obsd-tdep.c
index ceb8b5d85db..37be2d18032 100644
--- a/gdb/ppc-obsd-tdep.c
+++ b/gdb/ppc-obsd-tdep.c
@@ -231,7 +231,7 @@  ppcobsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_register (cache, this_frame, regnum);
 }
 
-static const struct frame_unwind ppcobsd_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy ppcobsd_sigtramp_frame_unwind (
   "ppc openbsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -240,7 +240,7 @@  static const struct frame_unwind ppcobsd_sigtramp_frame_unwind = {
   ppcobsd_sigtramp_frame_prev_register,
   NULL,
   ppcobsd_sigtramp_frame_sniffer
-};
+);
 
 
 static void
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index ab32f5057f9..3185596b961 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -785,11 +785,35 @@  pending_framepy_level (PyObject *self, PyObject *args)
   return gdb_py_object_from_longest (level).release ();
 }
 
+/* Class for frame unwinders registered by the Python architecture callback.  */
+class frame_unwind_python : public frame_unwind
+{
+public:
+  frame_unwind_python (const struct frame_data *newarch)
+    : frame_unwind ("python", NORMAL_FRAME, FRAME_UNWIND_EXTENSION, newarch)
+  { }
+
+  /* No need to override stop_reason, we want the default.  */
+
+  int sniffer (const frame_unwind *self, const frame_info_ptr &this_frame,
+		void **this_prologue_cache) const override;
+
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override;
+
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_prologue_cache,
+			       int regnum) const override;
+
+  void dealloc_cache (frame_info *self, void *this_cache) const override;
+};
+
 /* frame_unwind.this_id method.  */
 
-static void
-pyuw_this_id (const frame_info_ptr &this_frame, void **cache_ptr,
-	      struct frame_id *this_id)
+void
+frame_unwind_python::this_id (const frame_info_ptr &this_frame,
+			      void **cache_ptr,
+			      struct frame_id *this_id) const
 {
   *this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
   pyuw_debug_printf ("frame_id: %s", this_id->to_string ().c_str ());
@@ -797,9 +821,9 @@  pyuw_this_id (const frame_info_ptr &this_frame, void **cache_ptr,
 
 /* frame_unwind.prev_register.  */
 
-static struct value *
-pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
-		    int regnum)
+struct value *
+frame_unwind_python::prev_register (const frame_info_ptr &this_frame,
+				    void **cache_ptr, int regnum) const
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
 
@@ -820,13 +844,14 @@  pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
 
 /* Frame sniffer dispatch.  */
 
-static int
-pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
-	      void **cache_ptr)
+int
+frame_unwind_python::sniffer (const struct frame_unwind *self,
+			      const frame_info_ptr &this_frame,
+			      void **cache_ptr) const
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
 
-  struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
+  struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data ());
   cached_frame_info *cached_frame;
 
   gdbpy_enter enter_py (gdbarch);
@@ -947,8 +972,8 @@  pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
 
 /* Frame cache release shim.  */
 
-static void
-pyuw_dealloc_cache (frame_info *this_frame, void *cache)
+void
+frame_unwind_python::dealloc_cache (frame_info *this_frame, void *cache) const
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
   cached_frame_info *cached_frame = (cached_frame_info *) cache;
@@ -980,17 +1005,9 @@  pyuw_on_new_gdbarch (gdbarch *newarch)
   if (!data->unwinder_registered)
     {
       struct frame_unwind *unwinder
-	  = GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind);
-
-      unwinder->name = "python";
-      unwinder->type = NORMAL_FRAME;
-      unwinder->unwinder_class = FRAME_UNWIND_EXTENSION;
-      unwinder->stop_reason = default_frame_unwind_stop_reason;
-      unwinder->this_id = pyuw_this_id;
-      unwinder->prev_register = pyuw_prev_register;
-      unwinder->unwind_data = (const struct frame_data *) newarch;
-      unwinder->sniffer = pyuw_sniffer;
-      unwinder->dealloc_cache = pyuw_dealloc_cache;
+	  = obstack_new<frame_unwind_python>
+	    (gdbarch_obstack (newarch), (const struct frame_data *) newarch);
+
       frame_unwind_prepend_unwinder (newarch, unwinder);
       data->unwinder_registered = 1;
     }
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index a8ed0acf365..d390cceda05 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1940,8 +1940,7 @@  record_btrace_frame_dealloc_cache (frame_info *self, void *this_cache)
    Therefore this unwinder reports any possibly unwound registers as
    <unavailable>.  */
 
-const struct frame_unwind record_btrace_frame_unwind =
-{
+const struct frame_unwind_legacy record_btrace_frame_unwind (
   "record-btrace",
   NORMAL_FRAME,
   FRAME_UNWIND_GDB,
@@ -1951,10 +1950,9 @@  const struct frame_unwind record_btrace_frame_unwind =
   NULL,
   record_btrace_frame_sniffer,
   record_btrace_frame_dealloc_cache
-};
+);
 
-const struct frame_unwind record_btrace_tailcall_frame_unwind =
-{
+const struct frame_unwind_legacy record_btrace_tailcall_frame_unwind (
   "record-btrace tailcall",
   TAILCALL_FRAME,
   FRAME_UNWIND_GDB,
@@ -1964,7 +1962,7 @@  const struct frame_unwind record_btrace_tailcall_frame_unwind =
   NULL,
   record_btrace_tailcall_frame_sniffer,
   record_btrace_frame_dealloc_cache
-};
+);
 
 /* Implement the get_unwinder method.  */
 
diff --git a/gdb/record.h b/gdb/record.h
index aea6507c947..a5b4082b243 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -36,8 +36,8 @@  extern struct cmd_list_element *show_record_cmdlist;
 extern struct cmd_list_element *info_record_cmdlist;
 
 /* Unwinders for some record targets.  */
-extern const struct frame_unwind record_btrace_frame_unwind;
-extern const struct frame_unwind record_btrace_tailcall_frame_unwind;
+extern const struct frame_unwind_legacy record_btrace_frame_unwind;
+extern const struct frame_unwind_legacy record_btrace_tailcall_frame_unwind;
 
 /* A list of different recording methods.  */
 enum record_method
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 2a292cc4ff8..0801d076a38 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -3900,8 +3900,7 @@  riscv_frame_prev_register (const frame_info_ptr &this_frame,
    are the fallback unwinder (DWARF unwinder is used first), we use the
    default frame sniffer, which always accepts the frame.  */
 
-static const struct frame_unwind riscv_frame_unwind =
-{
+static const struct frame_unwind_legacy riscv_frame_unwind (
   /*.name          =*/ "riscv prologue",
   /*.type          =*/ NORMAL_FRAME,
   /*.unwinder_class=*/FRAME_UNWIND_ARCH,
@@ -3911,8 +3910,8 @@  static const struct frame_unwind riscv_frame_unwind =
   /*.unwind_data   =*/ NULL,
   /*.sniffer       =*/ default_frame_sniffer,
   /*.dealloc_cache =*/ NULL,
-  /*.prev_arch     =*/ NULL,
-};
+  /*.prev_arch     =*/ NULL
+);
 
 /* Extract a set of required target features out of ABFD.  If ABFD is
    nullptr then a RISCV_GDBARCH_FEATURES is returned in its default state.  */
diff --git a/gdb/rl78-tdep.c b/gdb/rl78-tdep.c
index 0fcd5f9f1f3..f4aecce055b 100644
--- a/gdb/rl78-tdep.c
+++ b/gdb/rl78-tdep.c
@@ -1183,8 +1183,7 @@  rl78_prev_register (const frame_info_ptr &this_frame,
     return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind rl78_unwind =
-{
+static const struct frame_unwind_legacy rl78_unwind (
   "rl78 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1193,7 +1192,7 @@  static const struct frame_unwind rl78_unwind =
   rl78_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Implement the "dwarf_reg_to_regnum" gdbarch method.  */
 
diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
index 3e5c8acff21..d161e2b6eae 100644
--- a/gdb/rs6000-aix-tdep.c
+++ b/gdb/rs6000-aix-tdep.c
@@ -328,7 +328,7 @@  aix_sighandle_frame_sniffer (const struct frame_unwind *self,
 
 /* AIX signal handler frame unwinder */
 
-static const struct frame_unwind aix_sighandle_frame_unwind = {
+static const struct frame_unwind_legacy aix_sighandle_frame_unwind (
   "rs6000 aix sighandle",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -337,7 +337,7 @@  static const struct frame_unwind aix_sighandle_frame_unwind = {
   aix_sighandle_frame_prev_register,
   NULL,
   aix_sighandle_frame_sniffer
-};
+);
 
 /* Core file support.  */
 
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 6dad9ccf98a..2885e910add 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -3837,8 +3837,7 @@  rs6000_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind rs6000_frame_unwind =
-{
+static const struct frame_unwind_legacy rs6000_frame_unwind (
   "rs6000 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3847,7 +3846,7 @@  static const struct frame_unwind rs6000_frame_unwind =
   rs6000_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Allocate and initialize a frame cache for an epilogue frame.
    SP is restored and prev-PC is stored in LR.  */
@@ -3979,8 +3978,7 @@  rs6000_epilogue_frame_sniffer (const struct frame_unwind *self,
 /* Frame unwinder for epilogue frame.  This is required for reverse step-over
    a function without debug information.  */
 
-static const struct frame_unwind rs6000_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy rs6000_epilogue_frame_unwind (
   "rs6000 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3988,7 +3986,7 @@  static const struct frame_unwind rs6000_epilogue_frame_unwind =
   rs6000_epilogue_frame_this_id, rs6000_epilogue_frame_prev_register,
   NULL,
   rs6000_epilogue_frame_sniffer
-};
+);
 
 
 static CORE_ADDR
diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c
index 6cb74c23a3c..18d184851cb 100644
--- a/gdb/rx-tdep.c
+++ b/gdb/rx-tdep.c
@@ -631,7 +631,7 @@  rx_exception_sniffer (const struct frame_unwind *self,
 /* Data structure for normal code using instruction-based prologue
    analyzer.  */
 
-static const struct frame_unwind rx_frame_unwind = {
+static const struct frame_unwind_legacy rx_frame_unwind (
   "rx prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -640,12 +640,12 @@  static const struct frame_unwind rx_frame_unwind = {
   rx_frame_prev_register,
   NULL,
   rx_frame_sniffer
-};
+);
 
 /* Data structure for exception code using instruction-based prologue
    analyzer.  */
 
-static const struct frame_unwind rx_exception_unwind = {
+static const struct frame_unwind_legacy rx_exception_unwind (
   "rx exception",
   /* SIGTRAMP_FRAME could be used here, but backtraces are less informative.  */
   NORMAL_FRAME,
@@ -655,7 +655,7 @@  static const struct frame_unwind rx_exception_unwind = {
   rx_frame_prev_register,
   NULL,
   rx_exception_sniffer
-};
+);
 
 /* Implement the "push_dummy_call" gdbarch method.  */
 static CORE_ADDR
diff --git a/gdb/s12z-tdep.c b/gdb/s12z-tdep.c
index 7fa73cf6b8c..faef1a42601 100644
--- a/gdb/s12z-tdep.c
+++ b/gdb/s12z-tdep.c
@@ -441,7 +441,7 @@  s12z_frame_prev_register (const frame_info_ptr &this_frame,
 }
 
 /* Data structures for the normal prologue-analysis-based unwinder.  */
-static const struct frame_unwind s12z_frame_unwind = {
+static const struct frame_unwind_legacy s12z_frame_unwind (
   "s12z prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -450,8 +450,8 @@  static const struct frame_unwind s12z_frame_unwind = {
   s12z_frame_prev_register,
   NULL,
   default_frame_sniffer,
-  NULL,
-};
+  NULL
+);
 
 
 constexpr gdb_byte s12z_break_insn[] = {0x00};
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 556fad64926..96d6d446219 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -541,7 +541,7 @@  s390_sigtramp_frame_sniffer (const struct frame_unwind *self,
 
 /* S390 sigtramp frame unwinder.  */
 
-static const struct frame_unwind s390_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy s390_sigtramp_frame_unwind (
   "s390 linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -550,7 +550,7 @@  static const struct frame_unwind s390_sigtramp_frame_unwind = {
   s390_sigtramp_frame_prev_register,
   NULL,
   s390_sigtramp_frame_sniffer
-};
+);
 
 /* Syscall handling.  */
 
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index bbf88dbcab1..55ce87d66a6 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -2646,7 +2646,7 @@  s390_frame_prev_register (const frame_info_ptr &this_frame,
 
 /* Default S390 frame unwinder.  */
 
-static const struct frame_unwind s390_frame_unwind = {
+static const struct frame_unwind_legacy s390_frame_unwind (
   "s390 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2655,7 +2655,7 @@  static const struct frame_unwind s390_frame_unwind = {
   s390_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Code stubs and their stack frames.  For things like PLTs and NULL
    function calls (where there is no true frame and the return address
@@ -2741,7 +2741,7 @@  s390_stub_frame_sniffer (const struct frame_unwind *self,
 
 /* S390 stub frame unwinder.  */
 
-static const struct frame_unwind s390_stub_frame_unwind = {
+static const struct frame_unwind_legacy s390_stub_frame_unwind (
   "s390 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2750,7 +2750,7 @@  static const struct frame_unwind s390_stub_frame_unwind = {
   s390_stub_frame_prev_register,
   NULL,
   s390_stub_frame_sniffer
-};
+);
 
 /* Frame base handling.  */
 
diff --git a/gdb/sentinel-frame.c b/gdb/sentinel-frame.c
index 9fe4036dbbe..c809259920d 100644
--- a/gdb/sentinel-frame.c
+++ b/gdb/sentinel-frame.c
@@ -78,8 +78,7 @@  sentinel_frame_prev_arch (const frame_info_ptr &this_frame,
   return cache->regcache->arch ();
 }
 
-const struct frame_unwind sentinel_frame_unwind =
-{
+const struct frame_unwind_legacy sentinel_frame_unwind (
   "sentinel",
   SENTINEL_FRAME,
   FRAME_UNWIND_GDB,
@@ -89,5 +88,5 @@  const struct frame_unwind sentinel_frame_unwind =
   NULL,
   NULL,
   NULL,
-  sentinel_frame_prev_arch,
-};
+  sentinel_frame_prev_arch
+);
diff --git a/gdb/sentinel-frame.h b/gdb/sentinel-frame.h
index 1a37ff1f49a..4b498f18940 100644
--- a/gdb/sentinel-frame.h
+++ b/gdb/sentinel-frame.h
@@ -34,6 +34,6 @@  extern void *sentinel_frame_cache (struct regcache *regcache);
 
 /* At present there is only one type of sentinel frame.  */
 
-extern const struct frame_unwind sentinel_frame_unwind;
+extern const struct frame_unwind_legacy sentinel_frame_unwind;
 
 #endif /* !defined (SENTINEL_FRAME_H)  */
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
index 5c23e5c43fc..05f85addf9c 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -1950,7 +1950,7 @@  sh_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
   *this_id = frame_id_build (cache->saved_sp, cache->pc);
 }
 
-static const struct frame_unwind sh_frame_unwind = {
+static const struct frame_unwind_legacy sh_frame_unwind (
   "sh prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1959,7 +1959,7 @@  static const struct frame_unwind sh_frame_unwind = {
   sh_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 sh_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
@@ -2017,8 +2017,7 @@  sh_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sh_stub_unwind =
-{
+static const struct frame_unwind_legacy sh_stub_unwind (
   "sh stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2027,7 +2026,7 @@  static const struct frame_unwind sh_stub_unwind =
   sh_frame_prev_register,
   NULL,
   sh_stub_unwind_sniffer
-};
+);
 
 /* Implement the stack_frame_destroyed_p gdbarch method.
 
diff --git a/gdb/sparc-netbsd-tdep.c b/gdb/sparc-netbsd-tdep.c
index 5b3dd067375..bc058dcbf63 100644
--- a/gdb/sparc-netbsd-tdep.c
+++ b/gdb/sparc-netbsd-tdep.c
@@ -248,8 +248,7 @@  sparc32nbsd_sigcontext_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc32nbsd_sigcontext_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32nbsd_sigcontext_frame_unwind (
   "sparc32 netbsd sigcontext",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -258,7 +257,7 @@  static const struct frame_unwind sparc32nbsd_sigcontext_frame_unwind =
   sparc32nbsd_sigcontext_frame_prev_register,
   NULL,
   sparc32nbsd_sigcontext_frame_sniffer
-};
+);
 
 /* Return the address of a system call's alternative return
    address.  */
diff --git a/gdb/sparc-obsd-tdep.c b/gdb/sparc-obsd-tdep.c
index 049c315fa14..2bc944efca0 100644
--- a/gdb/sparc-obsd-tdep.c
+++ b/gdb/sparc-obsd-tdep.c
@@ -134,8 +134,7 @@  sparc32obsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
 
   return 0;
 }
-static const struct frame_unwind sparc32obsd_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32obsd_sigtramp_frame_unwind (
   "sparc32 openbsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -144,7 +143,7 @@  static const struct frame_unwind sparc32obsd_sigtramp_frame_unwind =
   sparc32obsd_sigtramp_frame_prev_register,
   NULL,
   sparc32obsd_sigtramp_frame_sniffer
-};
+);
 
 
 
diff --git a/gdb/sparc-sol2-tdep.c b/gdb/sparc-sol2-tdep.c
index bb9c8a549b1..b6ac8011b7b 100644
--- a/gdb/sparc-sol2-tdep.c
+++ b/gdb/sparc-sol2-tdep.c
@@ -179,8 +179,7 @@  sparc32_sol2_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return sol2_sigtramp_p (this_frame);
 }
 
-static const struct frame_unwind sparc32_sol2_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32_sol2_sigtramp_frame_unwind (
   "sparc32 solaris sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -189,7 +188,7 @@  static const struct frame_unwind sparc32_sol2_sigtramp_frame_unwind =
   sparc32_sol2_sigtramp_frame_prev_register,
   NULL,
   sparc32_sol2_sigtramp_frame_sniffer
-};
+);
 
 
 
diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c
index 30dd061d8c9..307426ec25b 100644
--- a/gdb/sparc-tdep.c
+++ b/gdb/sparc-tdep.c
@@ -1346,8 +1346,7 @@  sparc32_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind sparc32_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32_frame_unwind (
   "sparc32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1356,7 +1355,7 @@  static const struct frame_unwind sparc32_frame_unwind =
   sparc32_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 
 static CORE_ADDR
diff --git a/gdb/sparc64-fbsd-tdep.c b/gdb/sparc64-fbsd-tdep.c
index 1d4c075a961..6630a9b51b0 100644
--- a/gdb/sparc64-fbsd-tdep.c
+++ b/gdb/sparc64-fbsd-tdep.c
@@ -196,8 +196,7 @@  sparc64fbsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64fbsd_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64fbsd_sigtramp_frame_unwind (
   "sparc64 freebsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -206,7 +205,7 @@  static const struct frame_unwind sparc64fbsd_sigtramp_frame_unwind =
   sparc64fbsd_sigtramp_frame_prev_register,
   NULL,
   sparc64fbsd_sigtramp_frame_sniffer
-};
+);
 
 
 static const struct regset sparc64fbsd_gregset =
diff --git a/gdb/sparc64-netbsd-tdep.c b/gdb/sparc64-netbsd-tdep.c
index 82eb99f57c1..53c49fe1130 100644
--- a/gdb/sparc64-netbsd-tdep.c
+++ b/gdb/sparc64-netbsd-tdep.c
@@ -222,8 +222,7 @@  sparc64nbsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64nbsd_sigcontext_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64nbsd_sigcontext_frame_unwind (
   "sparc64 netbsd sigcontext",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -232,7 +231,7 @@  static const struct frame_unwind sparc64nbsd_sigcontext_frame_unwind =
   sparc64nbsd_sigcontext_frame_prev_register,
   NULL,
   sparc64nbsd_sigtramp_frame_sniffer
-};
+);
 
 
 static const struct regset sparc64nbsd_gregset =
diff --git a/gdb/sparc64-obsd-tdep.c b/gdb/sparc64-obsd-tdep.c
index e81afa604e7..8596d2fa069 100644
--- a/gdb/sparc64-obsd-tdep.c
+++ b/gdb/sparc64-obsd-tdep.c
@@ -220,8 +220,7 @@  sparc64obsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64obsd_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64obsd_frame_unwind (
   "sparc64 openbsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -230,7 +229,7 @@  static const struct frame_unwind sparc64obsd_frame_unwind =
   sparc64obsd_frame_prev_register,
   NULL,
   sparc64obsd_sigtramp_frame_sniffer
-};
+);
 
 /* Kernel debugging support.  */
 
@@ -305,8 +304,7 @@  sparc64obsd_trapframe_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64obsd_trapframe_unwind =
-{
+static const struct frame_unwind_legacy sparc64obsd_trapframe_unwind (
   "sparc64 openbsd trap",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -315,7 +313,7 @@  static const struct frame_unwind sparc64obsd_trapframe_unwind =
   sparc64obsd_trapframe_prev_register,
   NULL,
   sparc64obsd_trapframe_sniffer
-};
+);
 
 
 /* Threads support.  */
diff --git a/gdb/sparc64-sol2-tdep.c b/gdb/sparc64-sol2-tdep.c
index e8d09e2516e..cadbf49788c 100644
--- a/gdb/sparc64-sol2-tdep.c
+++ b/gdb/sparc64-sol2-tdep.c
@@ -182,8 +182,7 @@  sparc64_sol2_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return sol2_sigtramp_p (this_frame);
 }
 
-static const struct frame_unwind sparc64_sol2_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64_sol2_sigtramp_frame_unwind (
   "sparc64 solaris sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -192,7 +191,7 @@  static const struct frame_unwind sparc64_sol2_sigtramp_frame_unwind =
   sparc64_sol2_sigtramp_frame_prev_register,
   NULL,
   sparc64_sol2_sigtramp_frame_sniffer
-};
+);
 
 
 
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index 09774ebcee6..6ad65f0b4b2 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -1135,8 +1135,7 @@  sparc64_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind sparc64_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64_frame_unwind (
   "sparc64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1145,7 +1144,7 @@  static const struct frame_unwind sparc64_frame_unwind =
   sparc64_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 
 static CORE_ADDR
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
index bac8ba84ed0..c7e42b154db 100644
--- a/gdb/tic6x-tdep.c
+++ b/gdb/tic6x-tdep.c
@@ -452,8 +452,7 @@  tic6x_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
   return info->base;
 }
 
-static const struct frame_unwind tic6x_frame_unwind =
-{
+static const struct frame_unwind_legacy tic6x_frame_unwind (
   "tic6x prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -462,7 +461,7 @@  static const struct frame_unwind tic6x_frame_unwind =
   tic6x_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static const struct frame_base tic6x_frame_base =
 {
@@ -516,8 +515,7 @@  tic6x_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind tic6x_stub_unwind =
-{
+static const struct frame_unwind_legacy tic6x_stub_unwind (
   "tic6x stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -526,7 +524,7 @@  static const struct frame_unwind tic6x_stub_unwind =
   tic6x_frame_prev_register,
   NULL,
   tic6x_stub_unwind_sniffer
-};
+);
 
 /* Return the instruction on address PC.  */
 
diff --git a/gdb/tilegx-tdep.c b/gdb/tilegx-tdep.c
index 16cd25f4635..e2247d769df 100644
--- a/gdb/tilegx-tdep.c
+++ b/gdb/tilegx-tdep.c
@@ -900,7 +900,7 @@  tilegx_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
   return cache->base;
 }
 
-static const struct frame_unwind tilegx_frame_unwind = {
+static const struct frame_unwind_legacy tilegx_frame_unwind (
   "tilegx prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -910,7 +910,7 @@  static const struct frame_unwind tilegx_frame_unwind = {
   NULL,                        /* const struct frame_data *unwind_data  */
   default_frame_sniffer,       /* frame_sniffer_ftype *sniffer  */
   NULL                         /* frame_prev_pc_ftype *prev_pc  */
-};
+);
 
 static const struct frame_base tilegx_frame_base = {
   &tilegx_frame_unwind,
diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c
index 6368f67a2e7..b1f3be66bc1 100644
--- a/gdb/tramp-frame.c
+++ b/gdb/tramp-frame.c
@@ -57,10 +57,45 @@  tramp_frame_cache (const frame_info_ptr &this_frame,
   return tramp_cache->trad_cache;
 }
 
-static void
-tramp_frame_this_id (const frame_info_ptr &this_frame,
-		     void **this_cache,
-		     struct frame_id *this_id)
+class frame_unwind_trampoline : public frame_unwind
+{
+private:
+  frame_prev_arch_ftype *prev_arch_p;
+public:
+  frame_unwind_trampoline (enum frame_type t, const struct frame_data *d,
+			   frame_prev_arch_ftype *pa)
+    : frame_unwind ("trampoline", t, FRAME_UNWIND_GDB, d), prev_arch_p (pa)
+  { }
+
+  int sniffer(const frame_unwind *self, const frame_info_ptr &this_frame,
+	      void **this_prologue_cache) const override;
+
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override;
+
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_prologue_cache,
+			       int regnum) const override;
+
+  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			     void **this_prologue_cache) const override
+  {
+    if (prev_arch_p == nullptr)
+      error (_("No prev_arch callback installed"));
+    return prev_arch_p (this_frame, this_prologue_cache);
+  }
+
+  /* FIXME: This should have a proper algorithm to deallocate the cache,
+     otherwise memory is leaked.  This method is empty here just so the
+     migration to c++ classes doesn't add regressions.  */
+  void dealloc_cache (frame_info *self, void *this_cache) const override
+  { }
+};
+
+void
+frame_unwind_trampoline::this_id (const frame_info_ptr &this_frame,
+				  void **this_cache,
+				  struct frame_id *this_id) const
 {
   struct trad_frame_cache *trad_cache
     = tramp_frame_cache (this_frame, this_cache);
@@ -68,10 +103,10 @@  tramp_frame_this_id (const frame_info_ptr &this_frame,
   trad_frame_get_id (trad_cache, this_id);
 }
 
-static struct value *
-tramp_frame_prev_register (const frame_info_ptr &this_frame,
-			   void **this_cache,
-			   int prev_regnum)
+struct value *
+frame_unwind_trampoline::prev_register (const frame_info_ptr &this_frame,
+					void **this_cache,
+					int prev_regnum) const
 {
   struct trad_frame_cache *trad_cache
     = tramp_frame_cache (this_frame, this_cache);
@@ -119,12 +154,12 @@  tramp_frame_start (const struct tramp_frame *tramp,
   return 0;
 }
 
-static int
-tramp_frame_sniffer (const struct frame_unwind *self,
-		     const frame_info_ptr &this_frame,
-		     void **this_cache)
+int
+frame_unwind_trampoline::sniffer (const struct frame_unwind *self,
+				  const frame_info_ptr &this_frame,
+				  void **this_cache) const
 {
-  const struct tramp_frame *tramp = self->unwind_data->tramp_frame;
+  const struct tramp_frame *tramp = self->unwind_data ()->tramp_frame;
   CORE_ADDR pc = get_frame_pc (this_frame);
   CORE_ADDR func;
   struct tramp_frame_cache *tramp_cache;
@@ -161,16 +196,11 @@  tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,
   gdb_assert (tramp_frame->insn_size <= sizeof (tramp_frame->insn[0].bytes));
 
   data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data);
-  unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind);
-
   data->tramp_frame = tramp_frame;
-  unwinder->type = tramp_frame->frame_type;
-  unwinder->unwind_data = data;
-  unwinder->unwinder_class = FRAME_UNWIND_GDB;
-  unwinder->sniffer = tramp_frame_sniffer;
-  unwinder->stop_reason = default_frame_unwind_stop_reason;
-  unwinder->this_id = tramp_frame_this_id;
-  unwinder->prev_register = tramp_frame_prev_register;
-  unwinder->prev_arch = tramp_frame->prev_arch;
+
+  unwinder = obstack_new <frame_unwind_trampoline> (gdbarch_obstack (gdbarch),
+						    tramp_frame->frame_type,
+						    data,
+						    tramp_frame->prev_arch);
   frame_unwind_prepend_unwinder (gdbarch, unwinder);
 }
diff --git a/gdb/v850-tdep.c b/gdb/v850-tdep.c
index 9300aeccf6f..17f7ee07e43 100644
--- a/gdb/v850-tdep.c
+++ b/gdb/v850-tdep.c
@@ -1320,7 +1320,7 @@  v850_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
   *this_id = frame_id_build (cache->saved_regs[E_SP_REGNUM].addr (), cache->pc);
 }
 
-static const struct frame_unwind v850_frame_unwind = {
+static const struct frame_unwind_legacy v850_frame_unwind (
   "v850 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1329,7 +1329,7 @@  static const struct frame_unwind v850_frame_unwind = {
   v850_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 v850_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/vax-tdep.c b/gdb/vax-tdep.c
index 25d07c59376..4660979928c 100644
--- a/gdb/vax-tdep.c
+++ b/gdb/vax-tdep.c
@@ -386,8 +386,7 @@  vax_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, cache->saved_regs, regnum);
 }
 
-static const struct frame_unwind vax_frame_unwind =
-{
+static const struct frame_unwind_legacy vax_frame_unwind (
   "vax prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -396,7 +395,7 @@  static const struct frame_unwind vax_frame_unwind =
   vax_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 
 static CORE_ADDR
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index 8a29a33042f..6d3b835ab36 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -1321,10 +1321,9 @@  cygwin_sigwrapper_frame_cache (frame_info_ptr this_frame, void **this_cache)
   return cache;
 }
 
-static struct value *
-cygwin_sigwrapper_frame_prev_register (const frame_info_ptr &this_frame,
-				       void **this_cache,
-				       int regnum)
+struct value *
+cygwin_sigwrapper_frame_unwind::prev_register
+    (const frame_info_ptr &this_frame, void **this_cache, int regnum) const
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   struct cygwin_sigwrapper_frame_cache *cache
@@ -1340,18 +1339,18 @@  cygwin_sigwrapper_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static void
-cygwin_sigwrapper_frame_this_id (const frame_info_ptr &this_frame,
-				 void **this_cache,
-				 struct frame_id *this_id)
+void
+cygwin_sigwrapper_frame_unwind::this_id (const frame_info_ptr &this_frame,
+					 void **this_cache,
+					 struct frame_id *this_id) const
 {
   *this_id = frame_id_build_unavailable_stack (get_frame_func (this_frame));
 }
 
-static int
-cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self_,
-				 const frame_info_ptr &this_frame,
-				 void **this_cache)
+int
+cygwin_sigwrapper_frame_unwind::sniffer (const struct frame_unwind *self_,
+					 const frame_info_ptr &this_frame,
+					 void **this_cache) const
 {
   const auto *self = (const struct cygwin_sigwrapper_frame_unwind *) self_;
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
@@ -1396,13 +1395,7 @@  cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self_,
 
 cygwin_sigwrapper_frame_unwind::cygwin_sigwrapper_frame_unwind
   (gdb::array_view<const gdb::array_view<const gdb_byte>> patterns_list)
-    : frame_unwind (),
-      patterns_list (patterns_list)
+    : frame_unwind ("cygwin sigwrapper", NORMAL_FRAME, FRAME_UNWIND_GDB,
+		    nullptr), patterns_list (patterns_list)
 {
-  name = "cygwin sigwrapper";
-  type = NORMAL_FRAME;
-  stop_reason = default_frame_unwind_stop_reason;
-  this_id = cygwin_sigwrapper_frame_this_id;
-  prev_register = cygwin_sigwrapper_frame_prev_register;
-  sniffer = cygwin_sigwrapper_frame_sniffer;
 }
diff --git a/gdb/windows-tdep.h b/gdb/windows-tdep.h
index f122f7aaa61..bdab0962ede 100644
--- a/gdb/windows-tdep.h
+++ b/gdb/windows-tdep.h
@@ -60,8 +60,9 @@  extern bool is_linked_with_cygwin_dll (bfd *abfd);
 /* Cygwin sigwapper unwinder.  Unwinds signal frames over
    sigbe/sigdelayed.  */
 
-struct cygwin_sigwrapper_frame_unwind : public frame_unwind
+class cygwin_sigwrapper_frame_unwind : public frame_unwind
 {
+public:
   explicit cygwin_sigwrapper_frame_unwind
     (gdb::array_view<const gdb::array_view<const gdb_byte>> patterns_list);
 
@@ -73,6 +74,19 @@  struct cygwin_sigwrapper_frame_unwind : public frame_unwind
      If any pattern in the list matches, then the frame is assumed to
      be a sigwrapper frame.  */
   gdb::array_view<const gdb::array_view<const gdb_byte>> patterns_list;
+
+  /* Calculate the frame ID of a cygwin wrapper.  */
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override;
+
+  /* Sniff the frame to tell if this unwinder should be used.  */
+  int sniffer (const frame_unwind *self, const frame_info_ptr &this_frame,
+	       void **this_prologue_cache) const override;
+
+  /* Calculate the value of a given register in the previous frame.  */
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_cache,
+			       int regnum) const override;
 };
 
 #endif
diff --git a/gdb/xstormy16-tdep.c b/gdb/xstormy16-tdep.c
index e01da4ecab4..e7f4e6fe600 100644
--- a/gdb/xstormy16-tdep.c
+++ b/gdb/xstormy16-tdep.c
@@ -728,7 +728,7 @@  xstormy16_frame_base_address (const frame_info_ptr &this_frame, void **this_cach
   return cache->base;
 }
 
-static const struct frame_unwind xstormy16_frame_unwind = {
+static const struct frame_unwind_legacy xstormy16_frame_unwind (
   "xstormy16 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -737,7 +737,7 @@  static const struct frame_unwind xstormy16_frame_unwind = {
   xstormy16_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static const struct frame_base xstormy16_frame_base = {
   &xstormy16_frame_unwind,
diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c
index b8b7efb38b0..98624f2be3c 100644
--- a/gdb/xtensa-tdep.c
+++ b/gdb/xtensa-tdep.c
@@ -1496,9 +1496,7 @@  xtensa_frame_prev_register (const frame_info_ptr &this_frame,
 }
 
 
-static const struct frame_unwind
-xtensa_unwind =
-{
+static const struct frame_unwind_legacy xtensa_unwind (
   "xtensa prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1507,7 +1505,7 @@  xtensa_unwind =
   xtensa_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 xtensa_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c
index 66a12cd3be7..c75b6ff3160 100644
--- a/gdb/z80-tdep.c
+++ b/gdb/z80-tdep.c
@@ -1063,9 +1063,7 @@  z80_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
   return 0;
 }
 
-static const struct frame_unwind
-z80_frame_unwind =
-{
+static const struct frame_unwind_legacy z80_frame_unwind (
   "z80",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1076,7 +1074,7 @@  z80_frame_unwind =
   default_frame_sniffer
   /*dealloc_cache*/
   /*prev_arch*/
-};
+);
 
 /* Initialize the gdbarch struct for the Z80 arch */
 static struct gdbarch *