Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Guinevere Larsen <guinevere@redhat.com>
To: Simon Marchi <simon.marchi@efficios.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH v2 4/4] gdb/solib: C++ify solib_ops
Date: Thu, 12 Jun 2025 09:56:06 -0300	[thread overview]
Message-ID: <fa840ac6-b0c1-41f4-ba87-9d0b5b326aac@redhat.com> (raw)
In-Reply-To: <20250609194146.137730-4-simon.marchi@efficios.com>

On 6/9/25 4:40 PM, Simon Marchi wrote:
> New in v2: added a precision in the commit message about some change in
> solib-rocm.c, about when / how rocm_solib_ops is set.
>
> Convert solib_ops into an abstract base class (with abstract methods,
> some of them with default implementations) and convert all the existing
> solib_ops instances to solib_ops derived classes / implementations.
>
> Prior to this patch, solib_ops is a structure holding function pointers,
> of which there are only a handful of global instances (in the
> `solib-*.c` files).  When passing an `solib_ops *` around, it's a
> pointer to one of these instances.  After this patch, there are no more
> global solib_ops instances.  Instances are created as needed and stored
> in struct program_space.  These instances could eventually be made to
> contain the program space-specific data, which is currently kept in
> per-program space registries (I have some pending patches for that).
>
> Prior to this patch, `gdbarch_so_ops` is a gdbarch method that returns a
> pointer to the appropriate solib_ops implementation for the gdbarch.
> This is replaced with the `gdbarch_new_solib_ops` method, which returns
> a new instance of the appropriate solib_ops implementation for this
> gdbarch.  This requires introducing some factory functions for the
> various solib_ops implementation, to be used as `gdbarch_new_solib_ops`
> callbacks.  For instance:
>
>      solib_ops_up
>      new_linux_ilp32_svr4_solib_ops ()
>      {
>        return std::make_unique<linux_ilp32_svr4_solib_ops> ();
>      }
>
> The previous code is full of cases of tdep files copying some base
> solib_ops implementation, and overriding one or more function pointer
> (see ppc_linux_init_abi, for instance).  I tried to convert all of this
> is a class hierarchy.  I like that it's now possible to get a good
> static view of all the existing solib_ops variants.  The hierarchy looks
> like this:
>
>      solib_ops
>      ├── aix_solib_ops
>      ├── darwin_solib_ops
>      ├── dsbt_solib_ops
>      ├── frv_solib_ops
>      ├── rocm_solib_ops
>      ├── svr4_solib_ops
>      │   ├── ilp32_svr4_solib_ops
>      │   ├── lp64_svr4_solib_ops
>      │   ├── linux_ilp32_svr4_solib_ops
>      │   │   ├── mips_linux_ilp32_svr4_solib_ops
>      │   │   └── ppc_linux_ilp32_svr4_solib_ops
>      │   ├── linux_lp64_svr4_solib_ops
>      │   │   └── mips_linux_lp64_svr4_solib_ops
>      │   ├── mips_nbsd_ilp32_svr4_solib_ops
>      │   ├── mips_nbsd_lp64_svr4_solib_ops
>      │   ├── mips_fbsd_ilp32_svr4_solib_ops
>      │   └── mips_fbsd_lp64_svr4_solib_ops
>      └── target_solib_ops
>          └── windows_solib_ops
>
> The solib-svr4 code has per-arch specialization to provide a
> link_map_offsets, containing the offsets of the interesting fields in
> `struct link_map` on that particular architecture.  Prior to this patch,
> arches would set a callback returning the appropriate link_map_offsets
> by calling `set_solib_svr4_fetch_link_map_offsets`, which also happened
> to set the gdbarch's so_ops to `&svr_so_ops`.  I converted this to an
> abstract virtual method of `struct svr4_solib_ops`, meaning that all
> classes deriving from svr4_solib_ops must provide a method returning the
> appropriate link_map_offsets for the architecture.  I renamed
> `set_solib_svr4_fetch_link_map_offsets` to `set_solib_svr4_ops`.  This
> function is still necessary because it also calls
> set_gdbarch_iterate_over_objfiles_in_search_order, but if it was not for
> that, we could get rid of it.
>
> There is a little "base class template" hack in mips-linux-tdep.c,
> because both mips_linux_ilp32_svr4_solib_ops and
> mips_linux_lp64_svr4_solib_ops need to derive from different SVR4 base
> classes (linux_ilp32_svr4_solib_ops and linux_lp64_svr4_solib_ops), but
> they both want to override the in_dynsym_resolve_code method with the
> same implementation.  Let me know if there's a more straightforward way
> to do this.
>
> The solib_ops::supports_namespaces method is new: the support for
> namespaces was previously predicated by the presence or absence of a
> find_solib_ns method.  It now needs to be explicit.
>
> There is a new progspace::release_solib_ops method, which is only needed
> for rocm_solib_ops.  For the moment, rocm_solib_ops replaces and wraps
> the existing svr4_solib_ops instance, in order to combine the results of
> the two.  The plan is to have a subsequent patch to allow program spaces to have
> multiple solib_ops, removing the need that release_solib_ops.
>
> Speaking of rocm_solib_ops: it previously overrode only a few methods by
> copying svr4_solib_ops and overwriting some function pointers.  Now, it
> needs to implement all the methods that svr4_solib_ops implements, in
> order to forward the call.  Otherwise, the default solib_ops method woul
> be called, hiding the svr4_solib_ops implementation.  Again, this can be
> removed once we have support for multiple solib_ops in a program_space.
>
> There is also a small change in how rocm_solib_ops is activated.  Prior
> to this patch, it's done at the end of rocm_update_solib_list.  Since it
> overrides the function pointer in the static svr4_solib_ops, and then
> overwrites the host gdbarch, so_ops field, it's something that happens
> only once.  After the patch though, we need to set rocm_solib_ops in all
> the program spaces that appear.  We do this in
> rocm_solib_target_inferior_created and in the new
> rocm_solib_target_inferior_execd.  After this, I will explore doing a
> change where rocm_solib_ops is only set when we detect the ROCm runtime
> is loaded.
>
> Change-Id: I5896b5bcbf8bdb024d67980380feba1ffefaa4c9
> ---

Hi!

I took a look over the entire series and the code looks good, and many 
of the changes are very good. I don't really feel like I can properly 
review this patch though, but I did ran it through aarch64 and ppc64le, 
and I see no issues, so

Tested-By: Guinevere Larsen <guinevere@redhat.com>

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

>   gdb/Makefile.in             |   3 +
>   gdb/aarch64-fbsd-tdep.c     |   3 +-
>   gdb/aarch64-linux-tdep.c    |   5 +-
>   gdb/alpha-linux-tdep.c      |   5 +-
>   gdb/alpha-netbsd-tdep.c     |   3 +-
>   gdb/alpha-obsd-tdep.c       |   3 +-
>   gdb/amd-dbgapi-target.c     |  18 +-
>   gdb/amd-dbgapi-target.h     |   5 +
>   gdb/amd64-darwin-tdep.c     |   2 +-
>   gdb/amd64-fbsd-tdep.c       |   3 +-
>   gdb/amd64-gnu-tdep.c        |   3 +-
>   gdb/amd64-linux-tdep.c      |   7 +-
>   gdb/amd64-netbsd-tdep.c     |   3 +-
>   gdb/amd64-obsd-tdep.c       |   3 +-
>   gdb/amd64-sol2-tdep.c       |   3 +-
>   gdb/arc-linux-tdep.c        |   4 +-
>   gdb/arm-fbsd-tdep.c         |   3 +-
>   gdb/arm-linux-tdep.c        |   4 +-
>   gdb/arm-netbsd-tdep.c       |   3 +-
>   gdb/arm-obsd-tdep.c         |   3 +-
>   gdb/configure.tgt           |  62 ++++--
>   gdb/cris-linux-tdep.c       |   5 +-
>   gdb/csky-linux-tdep.c       |   4 +-
>   gdb/dicos-tdep.c            |   2 +-
>   gdb/frv-tdep.c              |   3 +-
>   gdb/frv-tdep.h              |   3 -
>   gdb/gdbarch-gen.c           |  24 +--
>   gdb/gdbarch-gen.h           |   7 +-
>   gdb/gdbarch.h               |   1 +
>   gdb/gdbarch_components.py   |  12 +-
>   gdb/hppa-bsd-tdep.c         |   3 +-
>   gdb/hppa-linux-tdep.c       |   4 +-
>   gdb/i386-darwin-tdep.c      |   2 +-
>   gdb/i386-fbsd-tdep.c        |   3 +-
>   gdb/i386-gnu-tdep.c         |   3 +-
>   gdb/i386-linux-tdep.c       |   4 +-
>   gdb/i386-netbsd-tdep.c      |   3 +-
>   gdb/i386-obsd-tdep.c        |   3 +-
>   gdb/i386-sol2-tdep.c        |   3 +-
>   gdb/ia64-linux-tdep.c       |   4 +-
>   gdb/infcmd.c                |   2 +-
>   gdb/infrun.c                |   2 +-
>   gdb/linux-tdep.c            |  61 ------
>   gdb/linux-tdep.h            |   6 +-
>   gdb/loongarch-linux-tdep.c  |   8 +-
>   gdb/m32r-linux-tdep.c       |   4 +-
>   gdb/m68k-bsd-tdep.c         |   3 +-
>   gdb/m68k-linux-tdep.c       |   4 +-
>   gdb/microblaze-linux-tdep.c |   4 +-
>   gdb/mips-fbsd-tdep.c        |  57 ++++-
>   gdb/mips-linux-tdep.c       |  65 ++++--
>   gdb/mips-netbsd-tdep.c      |  54 ++++-
>   gdb/mips64-obsd-tdep.c      |   3 +-
>   gdb/mn10300-linux-tdep.c    |   4 +-
>   gdb/or1k-linux-tdep.c       |   4 +-
>   gdb/ppc-fbsd-tdep.c         |   6 +-
>   gdb/ppc-linux-tdep.c        |  46 ++--
>   gdb/ppc-netbsd-tdep.c       |   3 +-
>   gdb/ppc-obsd-tdep.c         |   3 +-
>   gdb/progspace.h             |  17 +-
>   gdb/riscv-fbsd-tdep.c       |   7 +-
>   gdb/riscv-linux-tdep.c      |   8 +-
>   gdb/rs6000-aix-tdep.c       |   2 +-
>   gdb/s390-linux-tdep.c       |   7 +-
>   gdb/sh-linux-tdep.c         |   4 +-
>   gdb/sh-netbsd-tdep.c        |   3 +-
>   gdb/solib-aix.c             |  67 +++---
>   gdb/solib-aix.h             |   7 +-
>   gdb/solib-darwin.c          |  65 +++---
>   gdb/solib-darwin.h          |   6 +-
>   gdb/solib-dsbt.c            |  59 ++---
>   gdb/solib-dsbt.h            |   6 +-
>   gdb/solib-frv.c             |  63 +++---
>   gdb/solib-frv.h             |  28 +++
>   gdb/solib-rocm.c            | 148 +++++++++----
>   gdb/solib-svr4-linux.c      |  98 +++++++++
>   gdb/solib-svr4-linux.h      |  47 ++++
>   gdb/solib-svr4.c            | 415 +++++++++++++++---------------------
>   gdb/solib-svr4.h            | 126 +++++++++--
>   gdb/solib-target.c          |  40 ++--
>   gdb/solib-target.h          |  16 +-
>   gdb/solib.c                 |  90 ++++----
>   gdb/solib.h                 | 137 +++++++-----
>   gdb/sparc-linux-tdep.c      |   4 +-
>   gdb/sparc-netbsd-tdep.c     |   3 +-
>   gdb/sparc-sol2-tdep.c       |   3 +-
>   gdb/sparc64-fbsd-tdep.c     |   3 +-
>   gdb/sparc64-linux-tdep.c    |   4 +-
>   gdb/sparc64-netbsd-tdep.c   |   3 +-
>   gdb/sparc64-obsd-tdep.c     |   3 +-
>   gdb/sparc64-sol2-tdep.c     |   3 +-
>   gdb/tic6x-linux-tdep.c      |   2 +-
>   gdb/tilegx-linux-tdep.c     |   7 +-
>   gdb/vax-netbsd-tdep.c       |   3 +-
>   gdb/windows-tdep.c          |  26 ++-
>   gdb/xtensa-linux-tdep.c     |   4 +-
>   gdb/xtensa-tdep.c           |   3 +-
>   97 files changed, 1225 insertions(+), 892 deletions(-)
>   create mode 100644 gdb/solib-frv.h
>   create mode 100644 gdb/solib-svr4-linux.c
>   create mode 100644 gdb/solib-svr4-linux.h
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 998203ce1e20..fc0c56564c23 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -891,6 +891,7 @@ ALL_TARGET_OBS = \
>   	solib-dsbt.o \
>   	solib-frv.o \
>   	solib-svr4.o \
> +	solib-svr4-linux.o \
>   	sparc-linux-tdep.o \
>   	sparc-netbsd-tdep.o \
>   	sparc-obsd-tdep.o \
> @@ -1478,7 +1479,9 @@ HFILES_NO_SRCDIR = \
>   	solib.h \
>   	solib-aix.h \
>   	solib-darwin.h \
> +	solib-frv.h \
>   	solib-svr4.h \
> +	solib-svr4-linux.h \
>   	solib-target.h \
>   	source.h \
>   	source-cache.h \
> diff --git a/gdb/aarch64-fbsd-tdep.c b/gdb/aarch64-fbsd-tdep.c
> index 07fa38a37285..7227456b7a6f 100644
> --- a/gdb/aarch64-fbsd-tdep.c
> +++ b/gdb/aarch64-fbsd-tdep.c
> @@ -239,8 +239,7 @@ aarch64_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     /* Generic FreeBSD support.  */
>     fbsd_init_abi (info, gdbarch);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   
>     tramp_frame_prepend_unwinder (gdbarch, &aarch64_fbsd_sigframe);
>   
> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
> index a194ac809c23..43b5e2a781f0 100644
> --- a/gdb/aarch64-linux-tdep.c
> +++ b/gdb/aarch64-linux-tdep.c
> @@ -23,6 +23,7 @@
>   #include "extract-store-integer.h"
>   #include "gdbarch.h"
>   #include "glibc-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "linux-tdep.h"
>   #include "svr4-tls-tdep.h"
>   #include "aarch64-tdep.h"
> @@ -2768,9 +2769,7 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->lowest_pc = 0x8000;
>   
>     linux_init_abi (info, gdbarch, 1);
> -
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>     /* Enable TLS support.  */
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
> diff --git a/gdb/alpha-linux-tdep.c b/gdb/alpha-linux-tdep.c
> index 2f6affa6a6d5..0ad731e567ed 100644
> --- a/gdb/alpha-linux-tdep.c
> +++ b/gdb/alpha-linux-tdep.c
> @@ -17,6 +17,7 @@
>      along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
>   
>   #include "frame.h"
> +#include "solib-svr4-linux.h"
>   #include "osabi.h"
>   #include "solib-svr4.h"
>   #include "symtab.h"
> @@ -369,9 +370,7 @@ alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->jb_elt_size = 8;
>   
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>     /* Enable TLS support.  */
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
> diff --git a/gdb/alpha-netbsd-tdep.c b/gdb/alpha-netbsd-tdep.c
> index a24003918bba..1c1dc66791e7 100644
> --- a/gdb/alpha-netbsd-tdep.c
> +++ b/gdb/alpha-netbsd-tdep.c
> @@ -264,8 +264,7 @@ alphanbsd_init_abi (struct gdbarch_info info,
>     set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
>   
>     /* NetBSD/alpha has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   
>     tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
>     tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp;
> diff --git a/gdb/alpha-obsd-tdep.c b/gdb/alpha-obsd-tdep.c
> index b5ddbbc61cf4..6d153bb1b413 100644
> --- a/gdb/alpha-obsd-tdep.c
> +++ b/gdb/alpha-obsd-tdep.c
> @@ -109,8 +109,7 @@ alphaobsd_init_abi(struct gdbarch_info info, struct gdbarch *gdbarch)
>     set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
>   
>     /* OpenBSD/alpha has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>     set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver);
>   
>     tdep->dynamic_sigtramp_offset = alphaobsd_sigtramp_offset;
> diff --git a/gdb/amd-dbgapi-target.c b/gdb/amd-dbgapi-target.c
> index 819b373da321..d7e9d040a557 100644
> --- a/gdb/amd-dbgapi-target.c
> +++ b/gdb/amd-dbgapi-target.c
> @@ -104,12 +104,26 @@ amd_dbgapi_lib_debug_module ()
>   
>   static gdb::observers::token amd_dbgapi_target_inferior_created_observer_token;
>   
> +/* See amd-dbgapi-target.h.  */
> +
>   const gdb::observers::token &
>   get_amd_dbgapi_target_inferior_created_observer_token ()
>   {
>     return amd_dbgapi_target_inferior_created_observer_token;
>   }
>   
> +/* inferior_execd observer token.  */
> +
> +static gdb::observers::token amd_dbgapi_target_inferior_execd_observer_token;
> +
> +/* See amd-dbgapi-target.h.  */
> +
> +const gdb::observers::token &
> +get_amd_dbgapi_target_inferior_execd_observer_token ()
> +{
> +  return amd_dbgapi_target_inferior_execd_observer_token;
> +}
> +
>   /* A type holding coordinates, etc. info for a given wave.  */
>   
>   struct wave_coordinates
> @@ -2511,7 +2525,9 @@ _initialize_amd_dbgapi_target ()
>     gdb::observers::inferior_created.attach
>       (amd_dbgapi_target_inferior_created,
>        amd_dbgapi_target_inferior_created_observer_token, "amd-dbgapi");
> -  gdb::observers::inferior_execd.attach (amd_dbgapi_inferior_execd, "amd-dbgapi");
> +  gdb::observers::inferior_execd.attach
> +    (amd_dbgapi_inferior_execd, amd_dbgapi_target_inferior_execd_observer_token,
> +     "amd-dbgapi");
>     gdb::observers::inferior_forked.attach (amd_dbgapi_inferior_forked, "amd-dbgapi");
>     gdb::observers::inferior_exit.attach (amd_dbgapi_inferior_exited, "amd-dbgapi");
>     gdb::observers::inferior_pre_detach.attach (amd_dbgapi_inferior_pre_detach, "amd-dbgapi");
> diff --git a/gdb/amd-dbgapi-target.h b/gdb/amd-dbgapi-target.h
> index dd37ba3b82d2..fe3a50ba33b0 100644
> --- a/gdb/amd-dbgapi-target.h
> +++ b/gdb/amd-dbgapi-target.h
> @@ -54,6 +54,11 @@ using is_amd_dbgapi_handle
>   const gdb::observers::token &
>     get_amd_dbgapi_target_inferior_created_observer_token ();
>   
> +/* Get the token of amd-dbgapi's inferior_execd observer.  */
> +
> +const gdb::observers::token &
> +  get_amd_dbgapi_target_inferior_execd_observer_token ();
> +
>   /* Comparison operators for amd-dbgapi handle types.  */
>   
>   template <typename T,
> diff --git a/gdb/amd64-darwin-tdep.c b/gdb/amd64-darwin-tdep.c
> index dde023e0fb08..f26ccc49f2c0 100644
> --- a/gdb/amd64-darwin-tdep.c
> +++ b/gdb/amd64-darwin-tdep.c
> @@ -113,7 +113,7 @@ x86_darwin_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     tdep->jb_pc_offset = 56;
>   
> -  set_gdbarch_so_ops (gdbarch, &darwin_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_darwin_solib_ops);
>   }
>   
>   void _initialize_amd64_darwin_tdep ();
> diff --git a/gdb/amd64-fbsd-tdep.c b/gdb/amd64-fbsd-tdep.c
> index eea01054aca0..b936a70bb54f 100644
> --- a/gdb/amd64-fbsd-tdep.c
> +++ b/gdb/amd64-fbsd-tdep.c
> @@ -328,8 +328,7 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   				     amd64fbsd_core_read_description);
>   
>     /* FreeBSD uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
>   					     svr4_fetch_objfile_link_map);
> diff --git a/gdb/amd64-gnu-tdep.c b/gdb/amd64-gnu-tdep.c
> index 602fa8e61160..3dfdf81adf8b 100644
> --- a/gdb/amd64-gnu-tdep.c
> +++ b/gdb/amd64-gnu-tdep.c
> @@ -218,8 +218,7 @@ amd64_gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->sc_num_regs = ARRAY_SIZE (amd64_gnu_sc_reg_offset);
>   
>     /* Hurd uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   }
>   
>   void _initialize_amd64_gnu_tdep ();
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index e5a2ab9dd526..8a44d233caa3 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -33,6 +33,7 @@
>   #include "amd64-linux-tdep.h"
>   #include "i386-linux-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "svr4-tls-tdep.h"
>   #include "gdbsupport/x86-xstate.h"
>   #include "inferior.h"
> @@ -2130,8 +2131,7 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->i386_syscall_record = amd64_linux_syscall_record;
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>     /* Register DTrace handlers.  */
>     set_gdbarch_dtrace_parse_probe_argument (gdbarch, amd64_dtrace_parse_probe_argument);
> @@ -2344,8 +2344,7 @@ amd64_x32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->i386_syscall_record = amd64_x32_linux_syscall_record;
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   }
>   
>   void _initialize_amd64_linux_tdep ();
> diff --git a/gdb/amd64-netbsd-tdep.c b/gdb/amd64-netbsd-tdep.c
> index f4464b796118..d1e088003dba 100644
> --- a/gdb/amd64-netbsd-tdep.c
> +++ b/gdb/amd64-netbsd-tdep.c
> @@ -116,8 +116,7 @@ amd64nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->sc_num_regs = ARRAY_SIZE (amd64nbsd_r_reg_offset);
>   
>     /* NetBSD uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   }
>   
>   void _initialize_amd64nbsd_tdep ();
> diff --git a/gdb/amd64-obsd-tdep.c b/gdb/amd64-obsd-tdep.c
> index 5acc380e7995..bd3d655a5e08 100644
> --- a/gdb/amd64-obsd-tdep.c
> +++ b/gdb/amd64-obsd-tdep.c
> @@ -443,8 +443,7 @@ amd64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     bsd_uthread_set_collect_uthread (gdbarch, amd64obsd_collect_uthread);
>   
>     /* OpenBSD uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   
>     /* Unwind kernel trap frames correctly.  */
>     frame_unwind_prepend_unwinder (gdbarch, &amd64obsd_trapframe_unwind);
> diff --git a/gdb/amd64-sol2-tdep.c b/gdb/amd64-sol2-tdep.c
> index 84d5f87b814a..7c9fa1965aeb 100644
> --- a/gdb/amd64-sol2-tdep.c
> +++ b/gdb/amd64-sol2-tdep.c
> @@ -96,8 +96,7 @@ amd64_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->sc_num_regs = tdep->gregset_num_regs;
>   
>     /* Solaris uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   }
>   
>   void _initialize_amd64_sol2_tdep ();
> diff --git a/gdb/arc-linux-tdep.c b/gdb/arc-linux-tdep.c
> index adf669151d1e..dfa5fc225b07 100644
> --- a/gdb/arc-linux-tdep.c
> +++ b/gdb/arc-linux-tdep.c
> @@ -19,6 +19,7 @@
>   
>   /* GDB header files.  */
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "objfiles.h"
>   #include "opcode/arc.h"
>   #include "osabi.h"
> @@ -736,8 +737,7 @@ arc_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux uses SVR4-style shared libraries, with 32-bit ints, longs
>        and pointers (ILP32).  */
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   }
>   
>   /* Suppress warning from -Wmissing-prototypes.  */
> diff --git a/gdb/arm-fbsd-tdep.c b/gdb/arm-fbsd-tdep.c
> index c9a466f91cd4..2e9024136f93 100644
> --- a/gdb/arm-fbsd-tdep.c
> +++ b/gdb/arm-fbsd-tdep.c
> @@ -300,8 +300,7 @@ arm_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     tramp_frame_prepend_unwinder (gdbarch, &arm_fbsd_sigframe);
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     tdep->jb_pc = 24;
>     tdep->jb_elt_size = 4;
> diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
> index 485a5d9cee36..316b3e733cef 100644
> --- a/gdb/arm-linux-tdep.c
> +++ b/gdb/arm-linux-tdep.c
> @@ -41,6 +41,7 @@
>   #include "arm-tdep.h"
>   #include "arm-linux-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "glibc-tdep.h"
>   #include "arch-utils.h"
>   #include "inferior.h"
> @@ -1801,8 +1802,7 @@ arm_linux_init_abi (struct gdbarch_info info,
>       }
>     tdep->jb_elt_size = ARM_LINUX_JB_ELEMENT_SIZE;
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* Single stepping.  */
>     set_gdbarch_software_single_step (gdbarch, arm_linux_software_single_step);
> diff --git a/gdb/arm-netbsd-tdep.c b/gdb/arm-netbsd-tdep.c
> index a16205436a9a..89fcbde10283 100644
> --- a/gdb/arm-netbsd-tdep.c
> +++ b/gdb/arm-netbsd-tdep.c
> @@ -156,8 +156,7 @@ arm_netbsd_elf_init_abi (struct gdbarch_info info,
>       tdep->fp_model = ARM_FLOAT_SOFT_VFP;
>   
>     /* NetBSD ELF uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   
>   void _initialize_arm_netbsd_tdep ();
> diff --git a/gdb/arm-obsd-tdep.c b/gdb/arm-obsd-tdep.c
> index 6fd4c8544719..a2ec923854ca 100644
> --- a/gdb/arm-obsd-tdep.c
> +++ b/gdb/arm-obsd-tdep.c
> @@ -83,8 +83,7 @@ armobsd_init_abi (struct gdbarch_info info,
>     tramp_frame_prepend_unwinder (gdbarch, &armobsd_sigframe);
>   
>     /* OpenBSD/arm uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>     set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver);
>   
>     tdep->jb_pc = 24;
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index e9b306809dc1..255c77e9f8c0 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -150,14 +150,15 @@ aarch64*-*-linux*)
>   			arch/aarch64-scalable-linux.o \
>   			arch/arm.o arch/arm-linux.o arch/arm-get-next-pcs.o \
>   			arm-tdep.o arm-linux-tdep.o \
> -			glibc-tdep.o linux-tdep.o solib-svr4.o svr4-tls-tdep.o \
> +			glibc-tdep.o linux-tdep.o solib-svr4.o \
> +			solib-svr4-linux.o svr4-tls-tdep.o \
>   			symfile-mem.o linux-record.o"
>   	;;
>   
>   alpha*-*-linux*)
>   	# Target: Little-endian Alpha running Linux
>   	gdb_target_obs="alpha-mdebug-tdep.o alpha-linux-tdep.o \
> -			linux-tdep.o solib-svr4.o"
> +			linux-tdep.o solib-svr4.o solib-svr4-linux.o"
>   	;;
>   alpha*-*-netbsd* | alpha*-*-knetbsd*-gnu)
>   	# Target: NetBSD/alpha
> @@ -179,7 +180,7 @@ amdgcn*-*-*)
>   am33_2.0*-*-linux*)
>   	# Target: Matsushita mn10300 (AM33) running Linux
>   	gdb_target_obs="mn10300-tdep.o mn10300-linux-tdep.o linux-tdep.o \
> -			solib-svr4.o"
> +			solib-svr4.o solib-svr4-linux.o"
>   	;;
>   
>   arc*-*-elf32)
> @@ -189,7 +190,8 @@ arc*-*-elf32)
>   
>   arc*-*-linux*)
>   	# Target: ARC machine running Linux
> -	gdb_target_obs="arc-linux-tdep.o linux-tdep.o solib-svr4.o"
> +	gdb_target_obs="arc-linux-tdep.o linux-tdep.o solib-svr4.o \
> +			solib-svr4-linux.o"
>   	;;
>   
>   arm*-wince-pe | arm*-*-mingw32ce*)
> @@ -199,7 +201,8 @@ arm*-wince-pe | arm*-*-mingw32ce*)
>   arm*-*-linux*)
>   	# Target: ARM based machine running GNU/Linux
>   	gdb_target_obs="arch/arm-linux.o arm-linux-tdep.o glibc-tdep.o \
> -			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o"
> +			solib-svr4.o solib-svr4-linux.o symfile-mem.o \
> +			linux-tdep.o linux-record.o"
>   	;;
>   arm*-*-freebsd*)
>   	# Target: FreeBSD/arm
> @@ -239,13 +242,14 @@ bpf-*-*)
>   
>   cris*)
>   	# Target: CRIS
> -	gdb_target_obs="cris-tdep.o cris-linux-tdep.o linux-tdep.o solib-svr4.o"
> +	gdb_target_obs="cris-tdep.o cris-linux-tdep.o linux-tdep.o \
> +		        solib-svr4.o solib-svr4-linux.o"
>   	;;
>   
>   csky*-*-linux*)
>   	# Target: CSKY running GNU/Linux
>   	gdb_target_obs="csky-tdep.o csky-linux-tdep.o glibc-tdep.o \
> -			linux-tdep.o solib-svr4.o"
> +			linux-tdep.o solib-svr4.o solib-svr4-linux.o"
>   	;;
>   
>   csky*-*-*)
> @@ -270,7 +274,8 @@ h8300-*-*)
>   hppa*-*-linux*)
>   	# Target: HP PA-RISC running Linux
>   	gdb_target_obs="hppa-linux-tdep.o glibc-tdep.o \
> -			linux-tdep.o solib-svr4.o symfile-mem.o"
> +			linux-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			symfile-mem.o"
>   	;;
>   hppa*-*-netbsd*)
>   	# Target: NetBSD/hppa
> @@ -315,7 +320,7 @@ i[34567]86-*-linux*)
>   	# Target: Intel 386 running GNU/Linux
>   	gdb_target_obs="i386-linux-tdep.o \
>   			glibc-tdep.o \
> -			solib-svr4.o symfile-mem.o \
> +			solib-svr4.o solib-svr4-linux.o symfile-mem.o \
>   			linux-tdep.o linux-record.o \
>   			arch/i386-linux-tdesc.o \
>   			arch/x86-linux-tdesc-features.o"
> @@ -345,7 +350,7 @@ i[34567]86-*-go32* | i[34567]86-*-msdosdjgpp*)
>   ia64-*-linux*)
>   	# Target: Intel IA-64 running GNU/Linux
>   	gdb_target_obs="ia64-linux-tdep.o linux-tdep.o \
> -			solib-svr4.o symfile-mem.o"
> +			solib-svr4.o solib-svr4-linux.o symfile-mem.o"
>   	;;
>   ia64-*-*vms*)
>   	# Target: Intel IA-64 running OpenVMS
> @@ -363,7 +368,8 @@ lm32-*-*)
>   loongarch*-*-linux*)
>   	# Target: LoongArch running Linux
>   	gdb_target_obs="loongarch-linux-tdep.o glibc-tdep.o \
> -			linux-tdep.o solib-svr4.o linux-record.o"
> +			linux-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			linux-record.o"
>   	;;
>   
>   m32c-*-*)
> @@ -374,8 +380,8 @@ m32c-*-*)
>   m32r*-*-linux*)
>   	# Target: Renesas M32R running GNU/Linux
>   	gdb_target_obs="m32r-tdep.o m32r-linux-tdep.o \
> -			glibc-tdep.o solib-svr4.o symfile-mem.o \
> -			linux-tdep.o"
> +			glibc-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			symfile-mem.o linux-tdep.o"
>   	;;
>   m32r*-*-*)
>   	# Target: Renesas m32r processor
> @@ -395,7 +401,8 @@ fido-*-elf*)
>   m68*-*-linux*)
>   	# Target: Motorola m68k with a.out and ELF
>   	gdb_target_obs="m68k-tdep.o m68k-linux-tdep.o solib-svr4.o \
> -			linux-tdep.o glibc-tdep.o symfile-mem.o"
> +			solib-svr4-linux.o linux-tdep.o glibc-tdep.o \
> +			symfile-mem.o"
>   	;;
>   m68*-*-netbsd* | m68*-*-knetbsd*-gnu)
>   	# Target: NetBSD/m68k
> @@ -415,7 +422,7 @@ mep-*-*)
>   microblaze*-linux-*|microblaze*-*-linux*)
>   	# Target: Xilinx MicroBlaze running Linux
>   	gdb_target_obs="microblaze-tdep.o microblaze-linux-tdep.o solib-svr4.o \
> -			symfile-mem.o linux-tdep.o"
> +			solib-svr4-linux.o symfile-mem.o linux-tdep.o"
>   	;;
>   microblaze*-*-*)
>   	# Target: Xilinx MicroBlaze running standalone
> @@ -425,7 +432,8 @@ microblaze*-*-*)
>   mips*-*-linux*)
>   	# Target: Linux/MIPS
>   	gdb_target_obs="mips-tdep.o mips-linux-tdep.o glibc-tdep.o \
> -			solib-svr4.o symfile-mem.o linux-tdep.o"
> +			solib-svr4.o solib-svr4-linux.o symfile-mem.o \
> +			linux-tdep.o"
>   	;;
>   mips*-*-netbsd* | mips*-*-knetbsd*-gnu)
>   	# Target: MIPS running NetBSD
> @@ -469,7 +477,8 @@ nds32*-*-elf)
>   or1k*-*-linux*)
>   	# Target: OpenCores OpenRISC 1000 32-bit running Linux
>   	gdb_target_obs="or1k-tdep.o or1k-linux-tdep.o solib-svr4.o \
> -			symfile-mem.o glibc-tdep.o linux-tdep.o"
> +			solib-svr4-linux.o symfile-mem.o glibc-tdep.o \
> +			linux-tdep.o"
>   	;;
>   
>   or1k-*-* | or1knd-*-*)
> @@ -503,7 +512,8 @@ powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*)
>   powerpc*-*-linux*)
>   	# Target: PowerPC running Linux
>   	gdb_target_obs="rs6000-tdep.o ppc-linux-tdep.o ppc-sysv-tdep.o \
> -			ppc64-tdep.o solib-svr4.o svr4-tls-tdep.o \
> +			ppc64-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			svr4-tls-tdep.o \
>   			glibc-tdep.o symfile-mem.o linux-tdep.o \
>   			ravenscar-thread.o ppc-ravenscar-thread.o \
>   			linux-record.o \
> @@ -524,6 +534,7 @@ powerpc*-*-*)
>   s390*-*-linux*)
>   	# Target: S390 running Linux
>   	gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \
> +			solib-svr4-linux.o \
>   			linux-tdep.o linux-record.o symfile-mem.o \
>   			svr4-tls-tdep.o"
>   	;;
> @@ -536,7 +547,8 @@ riscv*-*-freebsd*)
>   riscv*-*-linux*)
>   	# Target: Linux/RISC-V
>   	gdb_target_obs="riscv-linux-tdep.o riscv-canonicalize-syscall-gen.o \
> -			glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o \
> +			glibc-tdep.o linux-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			symfile-mem.o \
>   			linux-record.o svr4-tls-tdep.o"
>   	;;
>   
> @@ -558,7 +570,7 @@ rx-*-*)
>   sh*-*-linux*)
>   	# Target: GNU/Linux Super-H
>   	gdb_target_obs="sh-tdep.o sh-linux-tdep.o \
> -			solib-svr4.o symfile-mem.o \
> +			solib-svr4.o solib-svr4-linux.o symfile-mem.o \
>   			glibc-tdep.o linux-tdep.o"
>   	;;
>   sh*-*-netbsd* | sh*-*-knetbsd*-gnu)
> @@ -577,7 +589,8 @@ sh*)
>   sparc-*-linux*)
>   	# Target: GNU/Linux SPARC
>   	gdb_target_obs="sparc-tdep.o \
> -			sparc-linux-tdep.o solib-svr4.o symfile-mem.o \
> +			sparc-linux-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			symfile-mem.o \
>   			linux-tdep.o \
>   			ravenscar-thread.o sparc-ravenscar-thread.o"
>   	if test "x$have_64_bit_bfd" = "xyes"; then
> @@ -590,7 +603,8 @@ sparc64-*-linux*)
>   	# Target: GNU/Linux UltraSPARC
>   	gdb_target_obs="sparc64-tdep.o \
>   			sparc64-linux-tdep.o sparc-tdep.o \
> -			sparc-linux-tdep.o solib-svr4.o linux-tdep.o \
> +			sparc-linux-tdep.o solib-svr4.o solib-svr4-linux.o \
> +			linux-tdep.o \
>   			ravenscar-thread.o sparc-ravenscar-thread.o"
>   	;;
>   sparc*-*-freebsd* | sparc*-*-kfreebsd*-gnu)
> @@ -658,6 +672,7 @@ tic6x-*-*)
>   tilegx-*-linux*)
>   	# Target: TILE-Gx
>   	gdb_target_obs="tilegx-tdep.o tilegx-linux-tdep.o solib-svr4.o \
> +			solib-svr4-linux.o \
>   			symfile-mem.o glibc-tdep.o linux-tdep.o"
>   	;;
>   
> @@ -708,7 +723,8 @@ x86_64-*-linux*)
>   	# Target: GNU/Linux x86-64
>   	gdb_target_obs="amd64-linux-tdep.o ${i386_tobjs}  \
>   			i386-linux-tdep.o glibc-tdep.o svr4-tls-tdep.o \
> -			solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o \
> +			solib-svr4.o solib-svr4-linux.o symfile-mem.o \
> +			linux-tdep.o linux-record.o \
>   			arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o \
>   			arch/x86-linux-tdesc-features.o"
>   	;;
> diff --git a/gdb/cris-linux-tdep.c b/gdb/cris-linux-tdep.c
> index b2ac80d371c3..80e4c886fbfb 100644
> --- a/gdb/cris-linux-tdep.c
> +++ b/gdb/cris-linux-tdep.c
> @@ -23,6 +23,7 @@
>   
>   #include "osabi.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "solib-svr4.h"
>   #include "symtab.h"
>   #include "gdbarch.h"
> @@ -41,9 +42,7 @@ cris_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>       set_gdbarch_fetch_tls_load_module_address (gdbarch,
>   					       svr4_fetch_objfile_link_map);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> -
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   }
>   
>   void _initialize_cris_linux_tdep ();
> diff --git a/gdb/csky-linux-tdep.c b/gdb/csky-linux-tdep.c
> index 2afb35846bda..724542a10665 100644
> --- a/gdb/csky-linux-tdep.c
> +++ b/gdb/csky-linux-tdep.c
> @@ -22,6 +22,7 @@
>   #include "osabi.h"
>   #include "glibc-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "gdbarch.h"
>   #include "solib-svr4.h"
>   #include "regset.h"
> @@ -407,8 +408,7 @@ csky_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     /* Shared library handling.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
>     set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* Enable TLS support.  */
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
> diff --git a/gdb/dicos-tdep.c b/gdb/dicos-tdep.c
> index 4dfac76654e8..fd43d9d859e6 100644
> --- a/gdb/dicos-tdep.c
> +++ b/gdb/dicos-tdep.c
> @@ -27,7 +27,7 @@
>   void
>   dicos_init_abi (struct gdbarch *gdbarch)
>   {
> -  set_gdbarch_so_ops (gdbarch, &solib_target_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_target_solib_ops);
>   
>     /* Every process, although has its own address space, sees the same
>        list of shared libraries.  There's no "main executable" in DICOS,
> diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c
> index 360887247ebc..9baa9cc24c21 100644
> --- a/gdb/frv-tdep.c
> +++ b/gdb/frv-tdep.c
> @@ -25,6 +25,7 @@
>   #include "frame.h"
>   #include "frame-unwind.h"
>   #include "frame-base.h"
> +#include "solib-frv.h"
>   #include "trad-frame.h"
>   #include "dis-asm.h"
>   #include "sim-regno.h"
> @@ -1554,7 +1555,7 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>       set_gdbarch_convert_from_func_ptr_addr (gdbarch,
>   					    frv_convert_from_func_ptr_addr);
>   
> -  set_gdbarch_so_ops (gdbarch, &frv_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_frv_solib_ops);
>   
>     /* Hook in ABI-specific overrides, if they have been registered.  */
>     gdbarch_init_osabi (info, gdbarch);
> diff --git a/gdb/frv-tdep.h b/gdb/frv-tdep.h
> index 07982b40db0f..7b51b428e9c4 100644
> --- a/gdb/frv-tdep.h
> +++ b/gdb/frv-tdep.h
> @@ -118,7 +118,4 @@ CORE_ADDR frv_fdpic_find_canonical_descriptor (CORE_ADDR entry_point);
>      needed for TLS support.  */
>   CORE_ADDR frv_fetch_objfile_link_map (struct objfile *objfile);
>   
> -struct solib_ops;
> -extern const solib_ops frv_so_ops;
> -
>   #endif /* GDB_FRV_TDEP_H */
> diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
> index 32d16598940b..d191131d7b24 100644
> --- a/gdb/gdbarch-gen.c
> +++ b/gdb/gdbarch-gen.c
> @@ -157,7 +157,7 @@ struct gdbarch
>     gdbarch_single_step_through_delay_ftype *single_step_through_delay = nullptr;
>     gdbarch_print_insn_ftype *print_insn = default_print_insn;
>     gdbarch_skip_trampoline_code_ftype *skip_trampoline_code = generic_skip_trampoline_code;
> -  const solib_ops * so_ops = &solib_target_so_ops;
> +  gdbarch_new_solib_ops_ftype *new_solib_ops = new_target_solib_ops;
>     gdbarch_skip_solib_resolver_ftype *skip_solib_resolver = generic_skip_solib_resolver;
>     gdbarch_in_solib_return_trampoline_ftype *in_solib_return_trampoline = generic_in_solib_return_trampoline;
>     gdbarch_in_indirect_branch_thunk_ftype *in_indirect_branch_thunk = default_in_indirect_branch_thunk;
> @@ -425,7 +425,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>     /* Skip verify of single_step_through_delay, has predicate.  */
>     /* Skip verify of print_insn, invalid_p == 0.  */
>     /* Skip verify of skip_trampoline_code, invalid_p == 0.  */
> -  /* Skip verify of so_ops, invalid_p == 0.  */
> +  /* Skip verify of new_solib_ops, invalid_p == 0.  */
>     /* Skip verify of skip_solib_resolver, invalid_p == 0.  */
>     /* Skip verify of in_solib_return_trampoline, invalid_p == 0.  */
>     /* Skip verify of in_indirect_branch_thunk, invalid_p == 0.  */
> @@ -966,8 +966,8 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
>   	      "gdbarch_dump: skip_trampoline_code = <%s>\n",
>   	      host_address_to_string (gdbarch->skip_trampoline_code));
>     gdb_printf (file,
> -	      "gdbarch_dump: so_ops = %s\n",
> -	      host_address_to_string (gdbarch->so_ops));
> +	      "gdbarch_dump: new_solib_ops = <%s>\n",
> +	      host_address_to_string (gdbarch->new_solib_ops));
>     gdb_printf (file,
>   	      "gdbarch_dump: skip_solib_resolver = <%s>\n",
>   	      host_address_to_string (gdbarch->skip_solib_resolver));
> @@ -3469,21 +3469,21 @@ set_gdbarch_skip_trampoline_code (struct gdbarch *gdbarch,
>     gdbarch->skip_trampoline_code = skip_trampoline_code;
>   }
>   
> -const solib_ops *
> -gdbarch_so_ops (struct gdbarch *gdbarch)
> +solib_ops_up
> +gdbarch_new_solib_ops (struct gdbarch *gdbarch)
>   {
>     gdb_assert (gdbarch != NULL);
> -  /* Skip verify of so_ops, invalid_p == 0.  */
> +  gdb_assert (gdbarch->new_solib_ops != NULL);
>     if (gdbarch_debug >= 2)
> -    gdb_printf (gdb_stdlog, "gdbarch_so_ops called\n");
> -  return gdbarch->so_ops;
> +    gdb_printf (gdb_stdlog, "gdbarch_new_solib_ops called\n");
> +  return gdbarch->new_solib_ops ();
>   }
>   
>   void
> -set_gdbarch_so_ops (struct gdbarch *gdbarch,
> -		    const solib_ops * so_ops)
> +set_gdbarch_new_solib_ops (struct gdbarch *gdbarch,
> +			   gdbarch_new_solib_ops_ftype new_solib_ops)
>   {
> -  gdbarch->so_ops = so_ops;
> +  gdbarch->new_solib_ops = new_solib_ops;
>   }
>   
>   CORE_ADDR
> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
> index 313a8f198fdb..b5a3762f438e 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -818,10 +818,11 @@ typedef CORE_ADDR (gdbarch_skip_trampoline_code_ftype) (const frame_info_ptr &fr
>   extern CORE_ADDR gdbarch_skip_trampoline_code (struct gdbarch *gdbarch, const frame_info_ptr &frame, CORE_ADDR pc);
>   extern void set_gdbarch_skip_trampoline_code (struct gdbarch *gdbarch, gdbarch_skip_trampoline_code_ftype *skip_trampoline_code);
>   
> -/* Vtable of solib operations functions. */
> +/* Return a newly-allocated solib_ops object capable of providing the solibs for this architecture. */
>   
> -extern const solib_ops * gdbarch_so_ops (struct gdbarch *gdbarch);
> -extern void set_gdbarch_so_ops (struct gdbarch *gdbarch, const solib_ops * so_ops);
> +typedef solib_ops_up (gdbarch_new_solib_ops_ftype) ();
> +extern solib_ops_up gdbarch_new_solib_ops (struct gdbarch *gdbarch);
> +extern void set_gdbarch_new_solib_ops (struct gdbarch *gdbarch, gdbarch_new_solib_ops_ftype *new_solib_ops);
>   
>   /* If in_solib_dynsym_resolve_code() returns true, and SKIP_SOLIB_RESOLVER
>      evaluates non-zero, this is the address where the debugger will place
> diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
> index 9feb2cc24e5b..6accbd2fd5ee 100644
> --- a/gdb/gdbarch.h
> +++ b/gdb/gdbarch.h
> @@ -30,6 +30,7 @@
>   #include "displaced-stepping.h"
>   #include "gdbsupport/gdb-checked-static-cast.h"
>   #include "registry.h"
> +#include "solib.h"
>   
>   struct floatformat;
>   struct ui_file;
> diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> index ec09d9550889..000729494611 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -1431,12 +1431,12 @@ Function(
>       invalid=False,
>   )
>   
> -Value(
> -    comment="Vtable of solib operations functions.",
> -    type="const solib_ops *",
> -    name="so_ops",
> -    predefault="&solib_target_so_ops",
> -    printer="host_address_to_string (gdbarch->so_ops)",
> +Function(
> +    comment="Return a newly-allocated solib_ops object capable of providing the solibs for this architecture.",
> +    type="solib_ops_up",
> +    name="new_solib_ops",
> +    params=[],
> +    predefault="new_target_solib_ops",
>       invalid=False,
>   )
>   
> diff --git a/gdb/hppa-bsd-tdep.c b/gdb/hppa-bsd-tdep.c
> index db6c92f8aab5..f7269c921b55 100644
> --- a/gdb/hppa-bsd-tdep.c
> +++ b/gdb/hppa-bsd-tdep.c
> @@ -128,8 +128,7 @@ hppabsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     set_gdbarch_skip_trampoline_code (gdbarch, hppa_skip_trampoline_code);
>   
>     /* OpenBSD and NetBSD use SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     /* Hook in the DWARF CFI frame unwinder.  */
>     dwarf2_frame_set_init_reg (gdbarch, hppabsd_dwarf2_frame_init_reg);
> diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c
> index 16cdd4540165..b6a43ffa740c 100644
> --- a/gdb/hppa-linux-tdep.c
> +++ b/gdb/hppa-linux-tdep.c
> @@ -32,6 +32,7 @@
>   #include "regcache.h"
>   #include "hppa-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "elf/common.h"
>   
>   /* Map DWARF DBX register numbers to GDB register numbers.  */
> @@ -499,8 +500,7 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     frame_unwind_append_unwinder (gdbarch, &hppa_linux_sigtramp_frame_unwind);
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     tdep->in_solib_call_trampoline = hppa_in_solib_call_trampoline;
>     set_gdbarch_skip_trampoline_code (gdbarch, hppa_skip_trampoline_code);
> diff --git a/gdb/i386-darwin-tdep.c b/gdb/i386-darwin-tdep.c
> index 6180e0293a6e..9a1f39273942 100644
> --- a/gdb/i386-darwin-tdep.c
> +++ b/gdb/i386-darwin-tdep.c
> @@ -271,7 +271,7 @@ i386_darwin_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>        alignment.  */
>     set_gdbarch_long_double_bit (gdbarch, 128);
>   
> -  set_gdbarch_so_ops (gdbarch, &darwin_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_darwin_solib_ops);
>   }
>   
>   static enum gdb_osabi
> diff --git a/gdb/i386-fbsd-tdep.c b/gdb/i386-fbsd-tdep.c
> index 72237d89cfcc..5840b618e8fd 100644
> --- a/gdb/i386-fbsd-tdep.c
> +++ b/gdb/i386-fbsd-tdep.c
> @@ -402,8 +402,7 @@ i386fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   				     i386fbsd_core_read_description);
>   
>     /* FreeBSD uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
>   					     svr4_fetch_objfile_link_map);
> diff --git a/gdb/i386-gnu-tdep.c b/gdb/i386-gnu-tdep.c
> index 97fbc697b845..18fb2a6d2098 100644
> --- a/gdb/i386-gnu-tdep.c
> +++ b/gdb/i386-gnu-tdep.c
> @@ -180,8 +180,7 @@ i386gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* Hurd uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     /* Hurd uses the dynamic linker included in the GNU C Library.  */
>     set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
> diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
> index 4b05cc6870b5..93b518d93afc 100644
> --- a/gdb/i386-linux-tdep.c
> +++ b/gdb/i386-linux-tdep.c
> @@ -30,6 +30,7 @@
>   #include "i386-tdep.h"
>   #include "i386-linux-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "utils.h"
>   #include "glibc-tdep.h"
>   #include "solib-svr4.h"
> @@ -1461,8 +1462,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* GNU/Linux uses the dynamic linker included in the GNU C Library.  */
>     set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
> diff --git a/gdb/i386-netbsd-tdep.c b/gdb/i386-netbsd-tdep.c
> index 701ce048b5e8..d9a3e375d621 100644
> --- a/gdb/i386-netbsd-tdep.c
> +++ b/gdb/i386-netbsd-tdep.c
> @@ -415,8 +415,7 @@ i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     i386_elf_init_abi (info, gdbarch);
>   
>     /* NetBSD ELF uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     /* NetBSD ELF uses -fpcc-struct-return by default.  */
>     tdep->struct_return = pcc_struct_return;
> diff --git a/gdb/i386-obsd-tdep.c b/gdb/i386-obsd-tdep.c
> index be656688b369..0ea12d82f1b0 100644
> --- a/gdb/i386-obsd-tdep.c
> +++ b/gdb/i386-obsd-tdep.c
> @@ -441,8 +441,7 @@ i386obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     frame_unwind_prepend_unwinder (gdbarch, &i386obsd_trapframe_unwind);
>   
>     /* OpenBSD ELF uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   
>   void _initialize_i386obsd_tdep ();
> diff --git a/gdb/i386-sol2-tdep.c b/gdb/i386-sol2-tdep.c
> index e842236718d6..ffc95f41f0ca 100644
> --- a/gdb/i386-sol2-tdep.c
> +++ b/gdb/i386-sol2-tdep.c
> @@ -86,8 +86,7 @@ i386_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->sc_num_regs = tdep->gregset_num_regs;
>   
>     /* Solaris has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   \f
>   
> diff --git a/gdb/ia64-linux-tdep.c b/gdb/ia64-linux-tdep.c
> index c45b2bb26e89..d84848f1c35a 100644
> --- a/gdb/ia64-linux-tdep.c
> +++ b/gdb/ia64-linux-tdep.c
> @@ -26,6 +26,7 @@
>   #include "solib-svr4.h"
>   #include "symtab.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "regset.h"
>   
>   #include <ctype.h>
> @@ -235,8 +236,7 @@ ia64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>     /* Enable TLS support.  */
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 1adad5c3eaa3..6c67a918f57a 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -260,7 +260,7 @@ post_create_inferior (int from_tty, bool set_pspace_solib_ops)
>   
>     if (set_pspace_solib_ops)
>       current_program_space->set_solib_ops
> -      (*gdbarch_so_ops (current_inferior ()->arch ()));
> +      (gdbarch_new_solib_ops (current_inferior ()->arch ()));
>   
>     if (current_program_space->exec_bfd ())
>       {
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 5cdf66d26dcb..5c652045e8ac 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -1383,7 +1383,7 @@ follow_exec (ptid_t ptid, const char *exec_file_target)
>     target_find_description ();
>   
>     current_program_space->set_solib_ops
> -    (*gdbarch_so_ops (following_inferior->arch ()));
> +    (gdbarch_new_solib_ops (following_inferior->arch ()));
>     gdb::observers::inferior_execd.notify (execing_inferior, following_inferior);
>   
>     breakpoint_re_set ();
> diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
> index 1e339b59e2e8..12c05f31d53a 100644
> --- a/gdb/linux-tdep.c
> +++ b/gdb/linux-tdep.c
> @@ -3118,64 +3118,3 @@ more information about this file, refer to the manpage of proc(5) and core(5).")
>   			   &setlist, &showlist);
>   
>   }
> -
> -/* Fetch (and possibly build) an appropriate `link_map_offsets' for
> -   ILP32/LP64 Linux systems which don't have the r_ldsomap field.  */
> -
> -link_map_offsets *
> -linux_ilp32_fetch_link_map_offsets ()
> -{
> -  static link_map_offsets lmo;
> -  static link_map_offsets *lmp = nullptr;
> -
> -  if (lmp == nullptr)
> -    {
> -      lmp = &lmo;
> -
> -      lmo.r_version_offset = 0;
> -      lmo.r_version_size = 4;
> -      lmo.r_map_offset = 4;
> -      lmo.r_brk_offset = 8;
> -      lmo.r_ldsomap_offset = -1;
> -      lmo.r_next_offset = 20;
> -
> -      /* Everything we need is in the first 20 bytes.  */
> -      lmo.link_map_size = 20;
> -      lmo.l_addr_offset = 0;
> -      lmo.l_name_offset = 4;
> -      lmo.l_ld_offset = 8;
> -      lmo.l_next_offset = 12;
> -      lmo.l_prev_offset = 16;
> -    }
> -
> -  return lmp;
> -}
> -
> -link_map_offsets *
> -linux_lp64_fetch_link_map_offsets ()
> -{
> -  static link_map_offsets lmo;
> -  static link_map_offsets *lmp = nullptr;
> -
> -  if (lmp == nullptr)
> -    {
> -      lmp = &lmo;
> -
> -      lmo.r_version_offset = 0;
> -      lmo.r_version_size = 4;
> -      lmo.r_map_offset = 8;
> -      lmo.r_brk_offset = 16;
> -      lmo.r_ldsomap_offset = -1;
> -      lmo.r_next_offset = 40;
> -
> -      /* Everything we need is in the first 40 bytes.  */
> -      lmo.link_map_size = 40;
> -      lmo.l_addr_offset = 0;
> -      lmo.l_name_offset = 8;
> -      lmo.l_ld_offset = 16;
> -      lmo.l_next_offset = 24;
> -      lmo.l_prev_offset = 32;
> -    }
> -
> -  return lmp;
> -}
> diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
> index 7485fc132a63..3d82ea5bbdf3 100644
> --- a/gdb/linux-tdep.h
> +++ b/gdb/linux-tdep.h
> @@ -22,6 +22,7 @@
>   
>   #include "bfd.h"
>   #include "displaced-stepping.h"
> +#include "solib.h"
>   
>   struct inferior;
>   struct regcache;
> @@ -112,9 +113,4 @@ extern CORE_ADDR linux_get_hwcap2 (const std::optional<gdb::byte_vector> &auxv,
>   
>   extern CORE_ADDR linux_get_hwcap2 ();
>   
> -/* Fetch (and possibly build) an appropriate `struct link_map_offsets'
> -   for ILP32 and LP64 Linux systems.  */
> -extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets ();
> -extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets ();
> -
>   #endif /* GDB_LINUX_TDEP_H */
> diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c
> index 38485e04b1ec..af6474e8390b 100644
> --- a/gdb/loongarch-linux-tdep.c
> +++ b/gdb/loongarch-linux-tdep.c
> @@ -25,6 +25,7 @@
>   #include "inferior.h"
>   #include "linux-record.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "loongarch-tdep.h"
>   #include "record-full.h"
>   #include "regset.h"
> @@ -1145,10 +1146,9 @@ loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     linux_init_abi (info, gdbarch, 0);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 info.bfd_arch_info->bits_per_address == 32
> -					 ? linux_ilp32_fetch_link_map_offsets
> -					 : linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, (info.bfd_arch_info->bits_per_address == 32
> +				? new_linux_ilp32_svr4_solib_ops
> +				: new_linux_lp64_svr4_solib_ops));
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> diff --git a/gdb/m32r-linux-tdep.c b/gdb/m32r-linux-tdep.c
> index 4fbe7d9927c0..52061923f8eb 100644
> --- a/gdb/m32r-linux-tdep.c
> +++ b/gdb/m32r-linux-tdep.c
> @@ -36,6 +36,7 @@
>   
>   #include "m32r-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "gdbarch.h"
>   
>   \f
> @@ -461,8 +462,7 @@ m32r_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* Core file support.  */
>     set_gdbarch_iterate_over_regset_sections
> diff --git a/gdb/m68k-bsd-tdep.c b/gdb/m68k-bsd-tdep.c
> index 09c57c6e61da..8155f7d2fe06 100644
> --- a/gdb/m68k-bsd-tdep.c
> +++ b/gdb/m68k-bsd-tdep.c
> @@ -147,8 +147,7 @@ m68kbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     tdep->struct_return = pcc_struct_return;
>   
>     /* NetBSD ELF uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   
>   void _initialize_m68kbsd_tdep ();
> diff --git a/gdb/m68k-linux-tdep.c b/gdb/m68k-linux-tdep.c
> index cfc37abc59c6..baaf0d4c9740 100644
> --- a/gdb/m68k-linux-tdep.c
> +++ b/gdb/m68k-linux-tdep.c
> @@ -35,6 +35,7 @@
>   #include "observable.h"
>   #include "elf/common.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "regset.h"
>   \f
>   /* Offsets (in target ints) into jmp_buf.  */
> @@ -407,8 +408,7 @@ m68k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     /* Shared library handling.  */
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* GNU/Linux uses the dynamic linker included in the GNU C Library.  */
>     set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
> diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c
> index 8dcbeaafb834..9d18fbe7aa04 100644
> --- a/gdb/microblaze-linux-tdep.c
> +++ b/gdb/microblaze-linux-tdep.c
> @@ -35,6 +35,7 @@
>   #include "frame-unwind.h"
>   #include "tramp-frame.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   
>   static int
>   microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
> @@ -125,8 +126,7 @@ microblaze_linux_init_abi (struct gdbarch_info info,
>   					microblaze_linux_memory_remove_breakpoint);
>   
>     /* Shared library handling.  */
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* Trampolines.  */
>     tramp_frame_prepend_unwinder (gdbarch,
> diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c
> index c280527b6abb..f00ab31db3c9 100644
> --- a/gdb/mips-fbsd-tdep.c
> +++ b/gdb/mips-fbsd-tdep.c
> @@ -476,12 +476,28 @@ mips_fbsd_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc)
>     return fbsd_skip_solib_resolver (gdbarch, pc);
>   }
>   
> -/* FreeBSD/mips uses a slightly different `struct link_map' than the
> -   other FreeBSD platforms as it includes an additional `l_off'
> -   member.  */
> +/* solib_ops for ILP32 FreeBSD/MIPS systems.  */
>   
> -static struct link_map_offsets *
> -mips_fbsd_ilp32_fetch_link_map_offsets (void)
> +struct mips_fbsd_ilp32_solib_ops : public svr4_solib_ops
> +{
> +  /* FreeBSD/MIPS uses a slightly different `struct link_map' than the
> +     other FreeBSD platforms as it includes an additional `l_off' member.  */
> +
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
> +/* Return a new solib_ops for ILP32 FreeBSD/MIPS systems.  */
> +
> +static solib_ops_up
> +new_mips_fbsd_ilp32_solib_ops ()
> +{
> +  return std::make_unique<mips_fbsd_ilp32_solib_ops> ();
> +}
> +
> +/* See mips_fbsd_ilp32_solib_ops.  */
> +
> +link_map_offsets *
> +mips_fbsd_ilp32_solib_ops::fetch_link_map_offsets () const
>   {
>     static struct link_map_offsets lmo;
>     static struct link_map_offsets *lmp = NULL;
> @@ -508,8 +524,28 @@ mips_fbsd_ilp32_fetch_link_map_offsets (void)
>     return lmp;
>   }
>   
> -static struct link_map_offsets *
> -mips_fbsd_lp64_fetch_link_map_offsets (void)
> +/* solib_ops for LP64 FreeBSD/MIPS systems.  */
> +
> +struct mips_fbsd_lp64_solib_ops : public svr4_solib_ops
> +{
> +  /* FreeBSD/MIPS uses a slightly different `struct link_map' than the
> +     other FreeBSD platforms as it includes an additional `l_off' member.  */
> +
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
> +/* Return a new solib_ops for LP64 FreeBSD/MIPS systems.  */
> +
> +static solib_ops_up
> +new_mips_fbsd_lp64_solib_ops ()
> +{
> +  return std::make_unique<mips_fbsd_lp64_solib_ops> ();
> +}
> +
> +/* See mips_fbsd_lp64_solib_ops.  */
> +
> +link_map_offsets *
> +mips_fbsd_lp64_solib_ops::fetch_link_map_offsets () const
>   {
>     static struct link_map_offsets lmo;
>     static struct link_map_offsets *lmp = NULL;
> @@ -565,10 +601,9 @@ mips_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     set_gdbarch_skip_solib_resolver (gdbarch, mips_fbsd_skip_solib_resolver);
>   
>     /* FreeBSD/mips has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ?
> -	       mips_fbsd_ilp32_fetch_link_map_offsets :
> -	       mips_fbsd_lp64_fetch_link_map_offsets));
> +  set_solib_svr4_ops (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32
> +				? new_mips_fbsd_ilp32_solib_ops
> +				: new_mips_fbsd_lp64_solib_ops));
>   }
>   
>   void _initialize_mips_fbsd_tdep ();
> diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c
> index bf3df3697e15..29633d8298df 100644
> --- a/gdb/mips-linux-tdep.c
> +++ b/gdb/mips-linux-tdep.c
> @@ -36,6 +36,7 @@
>   #include "mips-linux-tdep.h"
>   #include "glibc-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "xml-syscall.h"
>   #include "gdbsupport/gdb_signals.h"
>   #include "inferior.h"
> @@ -45,8 +46,6 @@
>   #include "features/mips64-linux.c"
>   #include "features/mips64-dsp-linux.c"
>   
> -static solib_ops mips_svr4_so_ops;
> -
>   /* This enum represents the signals' numbers on the MIPS
>      architecture.  It just contains the signal definitions which are
>      different from the generic implementation.
> @@ -666,15 +665,22 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc)
>     return 1;
>   }
>   
> -/* Return true iff PC belongs to the dynamic linker resolution
> -   code, a PLT entry, or a lazy binding stub.  */
> +/* Mix-in class to add Linux/MIPS-specific methods to a base solib_ops
> +   class.  */
>   
> -static bool
> -mips_linux_in_dynsym_resolve_code (CORE_ADDR pc)
> +template <typename Base>
> +struct mips_linux_svr4_solib_ops : public Base
> +{
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> +};
> +
> +template <typename Base>
> +bool
> +mips_linux_svr4_solib_ops<Base>::in_dynsym_resolve_code (CORE_ADDR pc) const
>   {
>     /* Check whether PC is in the dynamic linker.  This also checks
>        whether it is in the .plt section, used by non-PIC executables.  */
> -  if (svr4_in_dynsym_resolve_code (pc))
> +  if (Base::in_dynsym_resolve_code (pc))
>       return true;
>   
>     /* Likewise for the stubs.  They live in the .MIPS.stubs section these
> @@ -686,6 +692,32 @@ mips_linux_in_dynsym_resolve_code (CORE_ADDR pc)
>     return false;
>   }
>   
> +/* solib_ops for ILP32 Linux/MIPS systems.  */
> +
> +using mips_linux_ilp32_svr4_solib_ops
> +  = mips_linux_svr4_solib_ops<linux_ilp32_svr4_solib_ops>;
> +
> +/* Return a new solib_ops for ILP32 Linux/MIPS systems.  */
> +
> +static solib_ops_up
> +new_mips_linux_ilp32_svr4_solib_ops ()
> +{
> +  return std::make_unique<mips_linux_ilp32_svr4_solib_ops> ();
> +}
> +
> +/* solib_ops for LP64 Linux/MIPS systems.  */
> +
> +using mips_linux_lp64_svr4_solib_ops
> +  = mips_linux_svr4_solib_ops<linux_lp64_svr4_solib_ops>;
> +
> +/* Return a new solib_ops for LP64 Linux/MIPS systems.  */
> +
> +static solib_ops_up
> +new_mips_linux_lp64_svr4_solib_ops ()
> +{
> +  return std::make_unique<mips_linux_lp64_svr4_solib_ops> ();
> +}
> +
>   /* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c,
>      and glibc_skip_solib_resolver in glibc-tdep.c.  The normal glibc
>      implementation of this triggers at "fixup" from the same objfile as
> @@ -1537,8 +1569,7 @@ mips_linux_init_abi (struct gdbarch_info info,
>         case MIPS_ABI_O32:
>   	set_gdbarch_get_longjmp_target (gdbarch,
>   					mips_linux_get_longjmp_target);
> -	set_solib_svr4_fetch_link_map_offsets
> -	  (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +	set_solib_svr4_ops (gdbarch, new_mips_linux_ilp32_svr4_solib_ops);
>   	tramp_frame_prepend_unwinder (gdbarch, &micromips_linux_o32_sigframe);
>   	tramp_frame_prepend_unwinder (gdbarch,
>   				      &micromips_linux_o32_rt_sigframe);
> @@ -1549,8 +1580,7 @@ mips_linux_init_abi (struct gdbarch_info info,
>         case MIPS_ABI_N32:
>   	set_gdbarch_get_longjmp_target (gdbarch,
>   					mips_linux_get_longjmp_target);
> -	set_solib_svr4_fetch_link_map_offsets
> -	  (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +	set_solib_svr4_ops (gdbarch, new_mips_linux_ilp32_svr4_solib_ops);
>   	set_gdbarch_long_double_bit (gdbarch, 128);
>   	set_gdbarch_long_double_format (gdbarch, floatformats_ieee_quad);
>   	tramp_frame_prepend_unwinder (gdbarch,
> @@ -1561,8 +1591,7 @@ mips_linux_init_abi (struct gdbarch_info info,
>         case MIPS_ABI_N64:
>   	set_gdbarch_get_longjmp_target (gdbarch,
>   					mips64_linux_get_longjmp_target);
> -	set_solib_svr4_fetch_link_map_offsets
> -	  (gdbarch, linux_lp64_fetch_link_map_offsets);
> +	set_solib_svr4_ops (gdbarch, new_mips_linux_lp64_svr4_solib_ops);
>   	set_gdbarch_long_double_bit (gdbarch, 128);
>   	set_gdbarch_long_double_format (gdbarch, floatformats_ieee_quad);
>   	tramp_frame_prepend_unwinder (gdbarch,
> @@ -1582,16 +1611,6 @@ mips_linux_init_abi (struct gdbarch_info info,
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
>   					     svr4_fetch_objfile_link_map);
>   
> -  /* Initialize this lazily, to avoid an initialization order
> -     dependency on solib-svr4.c's _initialize routine.  */
> -  if (mips_svr4_so_ops.in_dynsym_resolve_code == NULL)
> -    {
> -      mips_svr4_so_ops = svr4_so_ops;
> -      mips_svr4_so_ops.in_dynsym_resolve_code
> -	= mips_linux_in_dynsym_resolve_code;
> -    }
> -  set_gdbarch_so_ops (gdbarch, &mips_svr4_so_ops);
> -
>     set_gdbarch_write_pc (gdbarch, mips_linux_write_pc);
>   
>     set_gdbarch_core_read_description (gdbarch,
> diff --git a/gdb/mips-netbsd-tdep.c b/gdb/mips-netbsd-tdep.c
> index c9bdaa6800bb..165bf95c1881 100644
> --- a/gdb/mips-netbsd-tdep.c
> +++ b/gdb/mips-netbsd-tdep.c
> @@ -288,13 +288,27 @@ mipsnbsd_cannot_store_register (struct gdbarch *gdbarch, int regno)
>   	  || regno == mips_regnum (gdbarch)->fp_implementation_revision);
>   }
>   
> -/* Shared library support.  */
> +/* solib_ops for ILP32 NetBSD/MIPS systems.  */
>   
> -/* NetBSD/mips uses a slightly different `struct link_map' than the
> -   other NetBSD platforms.  */
> +struct mips_nbsd_ilp32_svr4_solib_ops : public svr4_solib_ops
> +{
> +  /* NetBSD/MIPS uses a slightly different `struct link_map' than the
> +     other NetBSD platforms.  */
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
>   
> -static struct link_map_offsets *
> -mipsnbsd_ilp32_fetch_link_map_offsets (void)
> +/* Return a new solib_ops for ILP32 NetBSD/MIPS systems.  */
> +
> +static solib_ops_up
> +new_mips_nbsd_ilp32_svr4_solib_ops ()
> +{
> +  return std::make_unique<mips_nbsd_ilp32_svr4_solib_ops> ();
> +}
> +
> +/* See mips_nbsd_ilp32_svr4_solib_ops.  */
> +
> +link_map_offsets *
> +mips_nbsd_ilp32_svr4_solib_ops::fetch_link_map_offsets () const
>   {
>     static struct link_map_offsets lmo;
>     static struct link_map_offsets *lmp = NULL;
> @@ -322,8 +336,27 @@ mipsnbsd_ilp32_fetch_link_map_offsets (void)
>     return lmp;
>   }
>   
> -static struct link_map_offsets *
> -mipsnbsd_lp64_fetch_link_map_offsets (void)
> +/* solib_ops for LP64 NetBSD/MIPS systems.  */
> +
> +struct mips_nbsd_lp64_svr4_solib_ops : public svr4_solib_ops
> +{
> +  /* NetBSD/MIPS uses a slightly different `struct link_map' than the
> +     other NetBSD platforms.  */
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
> +/* Return a new solib_ops for LP64 NetBSD/MIPS systems.  */
> +
> +static solib_ops_up
> +new_mips_nbsd_lp64_svr4_solib_ops ()
> +{
> +  return std::make_unique<mips_nbsd_lp64_svr4_solib_ops> ();
> +}
> +
> +/* See mips_nbsd_lp64_svr4_solib_ops.  */
> +
> +link_map_offsets *
> +mips_nbsd_lp64_svr4_solib_ops::fetch_link_map_offsets () const
>   {
>     static struct link_map_offsets lmo;
>     static struct link_map_offsets *lmp = NULL;
> @@ -369,10 +402,9 @@ mipsnbsd_init_abi (struct gdbarch_info info,
>     set_gdbarch_software_single_step (gdbarch, mips_software_single_step);
>   
>     /* NetBSD/mips has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ?
> -	       mipsnbsd_ilp32_fetch_link_map_offsets :
> -	       mipsnbsd_lp64_fetch_link_map_offsets));
> +  set_solib_svr4_ops (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32
> +				? new_mips_nbsd_ilp32_svr4_solib_ops
> +				: new_mips_nbsd_lp64_svr4_solib_ops));
>   }
>   
>   void _initialize_mipsnbsd_tdep ();
> diff --git a/gdb/mips64-obsd-tdep.c b/gdb/mips64-obsd-tdep.c
> index d0c9a1c86587..2d856c1442f2 100644
> --- a/gdb/mips64-obsd-tdep.c
> +++ b/gdb/mips64-obsd-tdep.c
> @@ -150,8 +150,7 @@ mips64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     obsd_init_abi(info, gdbarch);
>   
>     /* OpenBSD/mips64 has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   }
>   
>   void _initialize_mips64obsd_tdep ();
> diff --git a/gdb/mn10300-linux-tdep.c b/gdb/mn10300-linux-tdep.c
> index 3334ca0cc7d2..0fa5db972151 100644
> --- a/gdb/mn10300-linux-tdep.c
> +++ b/gdb/mn10300-linux-tdep.c
> @@ -29,6 +29,7 @@
>   #include "trad-frame.h"
>   #include "tramp-frame.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "gdbarch.h"
>   
>   /* Transliterated from <asm-mn10300/elf.h>...  */
> @@ -707,8 +708,7 @@ am33_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     set_gdbarch_iterate_over_regset_sections
>       (gdbarch, am33_iterate_over_regset_sections);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     tramp_frame_prepend_unwinder (gdbarch, &am33_linux_sigframe);
>     tramp_frame_prepend_unwinder (gdbarch, &am33_linux_rt_sigframe);
> diff --git a/gdb/or1k-linux-tdep.c b/gdb/or1k-linux-tdep.c
> index 4784e0b73e94..ca7562ee9a59 100644
> --- a/gdb/or1k-linux-tdep.c
> +++ b/gdb/or1k-linux-tdep.c
> @@ -20,6 +20,7 @@
>   #include "osabi.h"
>   #include "glibc-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "solib-svr4.h"
>   #include "regset.h"
>   #include "tramp-frame.h"
> @@ -144,8 +145,7 @@ or1k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   {
>     linux_init_abi (info, gdbarch, 0);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> diff --git a/gdb/ppc-fbsd-tdep.c b/gdb/ppc-fbsd-tdep.c
> index 3bab0318f487..5705bd96c314 100644
> --- a/gdb/ppc-fbsd-tdep.c
> +++ b/gdb/ppc-fbsd-tdep.c
> @@ -332,8 +332,7 @@ ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>         set_gdbarch_return_value (gdbarch, ppcfbsd_return_value);
>   
>         set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -      set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					     svr4_ilp32_fetch_link_map_offsets);
> +      set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>         frame_unwind_append_unwinder (gdbarch, &ppcfbsd_sigtramp_frame_unwind);
>         set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc");
> @@ -347,8 +346,7 @@ ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   					    ppc64_elf_make_msymbol_special);
>   
>         set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
> -      set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					     svr4_lp64_fetch_link_map_offsets);
> +      set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>         set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpc");
>       }
>   
> diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
> index b3cc4d5485e2..8f93d468bf99 100644
> --- a/gdb/ppc-linux-tdep.c
> +++ b/gdb/ppc-linux-tdep.c
> @@ -48,6 +48,7 @@
>   #include "arch-utils.h"
>   #include "xml-syscall.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "svr4-tls-tdep.h"
>   #include "linux-record.h"
>   #include "record-full.h"
> @@ -86,8 +87,6 @@
>   #include "features/rs6000/powerpc-e500l.c"
>   #include "dwarf2/frame.h"
>   
> -/* Shared library operations for PowerPC-Linux.  */
> -static solib_ops powerpc_so_ops;
>   
>   /* The syscall's XML filename for PPC and PPC64.  */
>   #define XML_SYSCALL_FILENAME_PPC "syscalls/ppc-linux.xml"
> @@ -306,16 +305,33 @@ static const struct ppc_insn_pattern powerpc32_plt_stub_so_2[] =
>   /* The max number of insns we check using ppc_insns_match_pattern.  */
>   #define POWERPC32_PLT_CHECK_LEN (ARRAY_SIZE (powerpc32_plt_stub) - 1)
>   
> -/* Check if PC is in PLT stub.  For non-secure PLT, stub is in .plt
> -   section.  For secure PLT, stub is in .text and we need to check
> -   instruction patterns.  */
> +/* solib_ops for ILP32 PowerPC/Linux systems.  */
>   
> -static bool
> -powerpc_linux_in_dynsym_resolve_code (CORE_ADDR pc)
> +struct ppc_linux_ilp32_svr4_solib_ops : public linux_ilp32_svr4_solib_ops
> +{
> +  /* Check if PC is in PLT stub.  For non-secure PLT, stub is in .plt
> +     section.  For secure PLT, stub is in .text and we need to check
> +     instruction patterns.  */
> +
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> +};
> +
> +/* Return a new solib_ops for ILP32 PowerPC/Linux systems.  */
> +
> +static solib_ops_up
> +new_ppc_linux_ilp32_svr4_solib_ops ()
> +{
> +  return std::make_unique<ppc_linux_ilp32_svr4_solib_ops> ();
> +}
> +
> +/* See ppc_linux_ilp32_svr4_solib_ops.  */
> +
> +bool
> +ppc_linux_ilp32_svr4_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
>   {
>     /* Check whether PC is in the dynamic linker.  This also checks
>        whether it is in the .plt section, used by non-PIC executables.  */
> -  if (svr4_in_dynsym_resolve_code (pc))
> +  if (linux_ilp32_svr4_solib_ops::in_dynsym_resolve_code (pc))
>       return true;
>   
>     /* Check if we are in the resolver.  */
> @@ -2265,8 +2281,6 @@ ppc_linux_init_abi (struct gdbarch_info info,
>   
>         /* Shared library handling.  */
>         set_gdbarch_skip_trampoline_code (gdbarch, ppc_skip_trampoline_code);
> -      set_solib_svr4_fetch_link_map_offsets
> -	(gdbarch, linux_ilp32_fetch_link_map_offsets);
>   
>         /* Setting the correct XML syscall filename.  */
>         set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_PPC);
> @@ -2283,14 +2297,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
>         else
>   	set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc");
>   
> -      if (powerpc_so_ops.in_dynsym_resolve_code == NULL)
> -	{
> -	  powerpc_so_ops = svr4_so_ops;
> -	  /* Override dynamic resolve function.  */
> -	  powerpc_so_ops.in_dynsym_resolve_code =
> -	    powerpc_linux_in_dynsym_resolve_code;
> -	}
> -      set_gdbarch_so_ops (gdbarch, &powerpc_so_ops);
> +      set_solib_svr4_ops (gdbarch, new_ppc_linux_ilp32_svr4_solib_ops);
>   
>         set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
>       }
> @@ -2317,8 +2324,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
>   
>         /* Shared library handling.  */
>         set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
> -      set_solib_svr4_fetch_link_map_offsets
> -	(gdbarch, linux_lp64_fetch_link_map_offsets);
> +      set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>         /* Setting the correct XML syscall filename.  */
>         set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_PPC64);
> diff --git a/gdb/ppc-netbsd-tdep.c b/gdb/ppc-netbsd-tdep.c
> index 911c012983e6..b030f93a2525 100644
> --- a/gdb/ppc-netbsd-tdep.c
> +++ b/gdb/ppc-netbsd-tdep.c
> @@ -179,8 +179,7 @@ ppcnbsd_init_abi (struct gdbarch_info info,
>     set_gdbarch_return_value (gdbarch, ppcnbsd_return_value);
>   
>     /* NetBSD uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     set_gdbarch_iterate_over_regset_sections
>       (gdbarch, ppcnbsd_iterate_over_regset_sections);
> diff --git a/gdb/ppc-obsd-tdep.c b/gdb/ppc-obsd-tdep.c
> index 5e271c1c7be3..dc00227a0c29 100644
> --- a/gdb/ppc-obsd-tdep.c
> +++ b/gdb/ppc-obsd-tdep.c
> @@ -254,8 +254,7 @@ ppcobsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     set_gdbarch_return_value (gdbarch, ppc_sysv_abi_broken_return_value);
>   
>     /* OpenBSD uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     set_gdbarch_iterate_over_regset_sections
>       (gdbarch, ppcobsd_iterate_over_regset_sections);
> diff --git a/gdb/progspace.h b/gdb/progspace.h
> index abb448195d74..c84a95c96657 100644
> --- a/gdb/progspace.h
> +++ b/gdb/progspace.h
> @@ -21,6 +21,7 @@
>   #ifndef GDB_PROGSPACE_H
>   #define GDB_PROGSPACE_H
>   
> +#include "solib.h"
>   #include "target.h"
>   #include "gdb_bfd.h"
>   #include "registry.h"
> @@ -234,19 +235,23 @@ struct program_space
>     /* Set this program space's solib provider.
>   
>        The solib provider must be unset prior to call this method.  */
> -  void set_solib_ops (const struct solib_ops &ops)
> +  void set_solib_ops (solib_ops_up ops)
>     {
>       gdb_assert (m_solib_ops == nullptr);
> -    m_solib_ops = &ops;
> +    m_solib_ops = std::move (ops);
>     };
>   
> -  /* Unset this program space's solib provider.  */
> +  /* Unset and free this program space's solib provider.  */
>     void unset_solib_ops ()
>     { m_solib_ops = nullptr; }
>   
> +  /* Unset and return this program space's solib provider.  */
> +  solib_ops_up release_solib_ops ()
> +  { return std::move (m_solib_ops); }
> +
>     /* Get this program space's solib provider.  */
> -  const struct solib_ops *solib_ops () const
> -  { return m_solib_ops; }
> +  struct solib_ops *solib_ops () const
> +  { return m_solib_ops.get (); }
>   
>     /* Return the list of all the solibs in this program space.  */
>     owning_intrusive_list<solib> &solibs ()
> @@ -373,7 +378,7 @@ struct program_space
>     owning_intrusive_list<objfile> m_objfiles_list;
>   
>     /* solib_ops implementation used to provide solibs in this program space.  */
> -  const struct solib_ops *m_solib_ops = nullptr;
> +  solib_ops_up m_solib_ops;
>   
>     /* List of shared objects mapped into this space.  Managed by
>        solib.c.  */
> diff --git a/gdb/riscv-fbsd-tdep.c b/gdb/riscv-fbsd-tdep.c
> index fcb91caf5fb6..69e14ac47e3a 100644
> --- a/gdb/riscv-fbsd-tdep.c
> +++ b/gdb/riscv-fbsd-tdep.c
> @@ -191,10 +191,9 @@ riscv_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     set_gdbarch_software_single_step (gdbarch, riscv_software_single_step);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 (riscv_isa_xlen (gdbarch) == 4
> -					  ? svr4_ilp32_fetch_link_map_offsets
> -					  : svr4_lp64_fetch_link_map_offsets));
> +  set_solib_svr4_ops (gdbarch, (riscv_isa_xlen (gdbarch) == 4
> +				? new_svr4_ilp32_solib_ops
> +				: new_svr4_lp64_solib_ops));
>   
>     tramp_frame_prepend_unwinder (gdbarch, &riscv_fbsd_sigframe);
>   
> diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
> index e1ea6158a45c..fb21fa0cfa1a 100644
> --- a/gdb/riscv-linux-tdep.c
> +++ b/gdb/riscv-linux-tdep.c
> @@ -20,6 +20,7 @@
>   #include "osabi.h"
>   #include "glibc-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "svr4-tls-tdep.h"
>   #include "solib-svr4.h"
>   #include "regset.h"
> @@ -512,10 +513,9 @@ riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     set_gdbarch_software_single_step (gdbarch, riscv_software_single_step);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 (riscv_isa_xlen (gdbarch) == 4
> -					  ? linux_ilp32_fetch_link_map_offsets
> -					  : linux_lp64_fetch_link_map_offsets));
> +  set_solib_svr4_ops (gdbarch, (riscv_isa_xlen (gdbarch) == 4
> +				? new_linux_ilp32_svr4_solib_ops
> +				: new_linux_lp64_svr4_solib_ops));
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
> index ab9feb38fb78..214fb88c32cc 100644
> --- a/gdb/rs6000-aix-tdep.c
> +++ b/gdb/rs6000-aix-tdep.c
> @@ -1411,7 +1411,7 @@ rs6000_aix_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     set_gdbarch_wchar_signed (gdbarch, 0);
>     set_gdbarch_auto_wide_charset (gdbarch, rs6000_aix_auto_wide_charset);
>   
> -  set_gdbarch_so_ops (gdbarch, &solib_aix_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_aix_solib_ops);
>     frame_unwind_append_unwinder (gdbarch, &aix_sighandle_frame_unwind);
>   }
>   
> diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
> index bd1f42ca8d25..e22e1454bed6 100644
> --- a/gdb/s390-linux-tdep.c
> +++ b/gdb/s390-linux-tdep.c
> @@ -29,6 +29,7 @@
>   #include "gdbcore.h"
>   #include "linux-record.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "svr4-tls-tdep.h"
>   #include "objfiles.h"
>   #include "osabi.h"
> @@ -1214,8 +1215,7 @@ s390_linux_init_abi_31 (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     s390_linux_init_abi_any (info, gdbarch);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>     set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_S390);
>   }
>   
> @@ -1230,8 +1230,7 @@ s390_linux_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     s390_linux_init_abi_any (info, gdbarch);
>   
> -  set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					 linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>     set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_S390X);
>   }
>   
> diff --git a/gdb/sh-linux-tdep.c b/gdb/sh-linux-tdep.c
> index f0b35d3063a6..c28ec361eb05 100644
> --- a/gdb/sh-linux-tdep.c
> +++ b/gdb/sh-linux-tdep.c
> @@ -28,6 +28,7 @@
>   #include "glibc-tdep.h"
>   #include "sh-tdep.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "gdbarch.h"
>   
>   #define REGSx16(base) \
> @@ -187,8 +188,7 @@ sh_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>     set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
>   
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
> diff --git a/gdb/sh-netbsd-tdep.c b/gdb/sh-netbsd-tdep.c
> index f99566f2fd59..685ba3c0585d 100644
> --- a/gdb/sh-netbsd-tdep.c
> +++ b/gdb/sh-netbsd-tdep.c
> @@ -68,8 +68,7 @@ shnbsd_init_abi (struct gdbarch_info info,
>     tdep->core_gregmap = (struct sh_corefile_regmap *)regmap;
>     tdep->sizeof_gregset = 84;
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   
>   void _initialize_shnbsd_tdep ();
> diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c
> index 17eeba940770..91cc1d8c66de 100644
> --- a/gdb/solib-aix.c
> +++ b/gdb/solib-aix.c
> @@ -24,6 +24,24 @@
>   #include "xcoffread.h"
>   #include "observable.h"
>   
> +/* solib_ops for AIX systems.  */
> +
> +struct aix_solib_ops : public solib_ops
> +{
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  void create_inferior_hook (int from_tty) override;
> +  owning_intrusive_list<solib> current_sos () override;
> +  gdb_bfd_ref_ptr bfd_open (const char *pathname) override;
> +};
> +
> +/* See solib-aix.h.  */
> +
> +solib_ops_up
> +new_aix_solib_ops ()
> +{
> +  return std::make_unique<aix_solib_ops> ();
> +}
> +
>   /* Our private data in struct solib.  */
>   
>   struct lm_info_aix final : public lm_info
> @@ -306,10 +324,9 @@ solib_aix_bss_data_overlap (bfd *abfd)
>     return 0;
>   }
>   
> -/* Implement the "relocate_section_addresses" solib_ops method.  */
> -
> -static void
> -solib_aix_relocate_section_addresses (solib &so, target_section *sec)
> +void
> +aix_solib_ops::relocate_section_addresses (solib &so,
> +					   target_section *sec) const
>   {
>     struct bfd_section *bfd_sect = sec->the_bfd_section;
>     bfd *abfd = bfd_sect->owner;
> @@ -410,10 +427,8 @@ solib_aix_get_section_offsets (struct objfile *objfile,
>     return offsets;
>   }
>   
> -/* Implement the "solib_create_inferior_hook" solib_ops method.  */
> -
> -static void
> -solib_aix_solib_create_inferior_hook (int from_tty)
> +void
> +aix_solib_ops::create_inferior_hook (int from_tty)
>   {
>     const char *warning_msg = "unable to relocate main executable";
>   
> @@ -441,10 +456,8 @@ solib_aix_solib_create_inferior_hook (int from_tty)
>       }
>   }
>   
> -/* Implement the "current_sos" solib_ops method.  */
> -
> -static owning_intrusive_list<solib>
> -solib_aix_current_sos ()
> +owning_intrusive_list<solib>
> +aix_solib_ops::current_sos ()
>   {
>     std::optional<std::vector<lm_info_aix>> &library_list
>       = solib_aix_get_library_list (current_inferior (), NULL);
> @@ -480,7 +493,7 @@ solib_aix_current_sos ()
>   	}
>   
>         /* Add it to the list.  */
> -      auto &new_solib = sos.emplace_back (solib_aix_so_ops);
> +      auto &new_solib = sos.emplace_back (*this);
>         new_solib.original_name = so_name;
>         new_solib.name = so_name;
>         new_solib.lm_info = std::make_unique<lm_info_aix> (info);
> @@ -489,10 +502,8 @@ solib_aix_current_sos ()
>     return sos;
>   }
>   
> -/* Implement the "bfd_open" solib_ops method.  */
> -
> -static gdb_bfd_ref_ptr
> -solib_aix_bfd_open (const char *pathname)
> +gdb_bfd_ref_ptr
> +aix_solib_ops::bfd_open (const char *pathname)
>   {
>     /* The pathname is actually a synthetic filename with the following
>        form: "/path/to/sharedlib(member.o)" (double-quotes excluded).
> @@ -506,7 +517,7 @@ solib_aix_bfd_open (const char *pathname)
>     int found_file;
>   
>     if (pathname[path_len - 1] != ')')
> -    return solib_bfd_open (pathname);
> +    return solib_ops::bfd_open (pathname);
>   
>     /* Search for the associated parens.  */
>     sep = strrchr (pathname, '(');
> @@ -516,7 +527,7 @@ solib_aix_bfd_open (const char *pathname)
>   	 to open pathname without decoding, possibly leading to
>   	 a failure), rather than triggering an assert failure).  */
>         warning (_("missing '(' in shared object pathname: %s"), pathname);
> -      return solib_bfd_open (pathname);
> +      return solib_ops::bfd_open (pathname);
>       }
>     filename_len = sep - pathname;
>   
> @@ -659,24 +670,6 @@ solib_aix_normal_stop_observer (struct bpstat *unused_1, int unused_2)
>     data->library_list.reset ();
>   }
>   
> -/* The solib_ops for AIX targets.  */
> -const solib_ops solib_aix_so_ops =
> -{
> -  solib_aix_relocate_section_addresses,
> -  nullptr,
> -  nullptr,
> -  solib_aix_solib_create_inferior_hook,
> -  solib_aix_current_sos,
> -  nullptr,
> -  nullptr,
> -  solib_aix_bfd_open,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  default_find_solib_addr,
> -};
> -
>   void _initialize_solib_aix ();
>   void
>   _initialize_solib_aix ()
> diff --git a/gdb/solib-aix.h b/gdb/solib-aix.h
> index 7a1bc7b4b567..ac824e547cfa 100644
> --- a/gdb/solib-aix.h
> +++ b/gdb/solib-aix.h
> @@ -18,9 +18,12 @@
>   #ifndef GDB_SOLIB_AIX_H
>   #define GDB_SOLIB_AIX_H
>   
> -struct solib_ops;
> -extern const solib_ops solib_aix_so_ops;
> +#include "solib.h"
>   
>   extern CORE_ADDR solib_aix_get_toc_value (CORE_ADDR pc);
>   
> +/* Return a new solib_ops for AIX systems.  */
> +
> +solib_ops_up new_aix_solib_ops ();
> +
>   #endif /* GDB_SOLIB_AIX_H */
> diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c
> index 88a2962f5dd5..aece190a711d 100644
> --- a/gdb/solib-darwin.c
> +++ b/gdb/solib-darwin.c
> @@ -33,6 +33,25 @@
>   #include "mach-o.h"
>   #include "mach-o/external.h"
>   
> +/* solib_ops for Darwin systems.  */
> +
> +struct darwin_solib_ops : public solib_ops
> +{
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  void clear_solib (program_space *pspace) override;
> +  void create_inferior_hook (int from_tty) override;
> +  owning_intrusive_list<solib> current_sos () override;
> +  gdb_bfd_ref_ptr bfd_open (const char *pathname) override;
> +};
> +
> +/* See solib-darwin.h.  */
> +
> +solib_ops_up
> +new_darwin_solib_ops ()
> +{
> +  return std::make_unique<darwin_solib_ops> ();
> +}
> +
>   struct gdb_dyld_image_info
>   {
>     /* Base address (which corresponds to the Mach-O header).  */
> @@ -188,10 +207,8 @@ find_program_interpreter (void)
>     return buf;
>   }
>   
> -/* Build a list of currently loaded shared objects.  See solib-svr4.c.  */
> -
> -static owning_intrusive_list<solib>
> -darwin_current_sos ()
> +owning_intrusive_list<solib>
> +darwin_solib_ops::current_sos ()
>   {
>     type *ptr_type
>       = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
> @@ -250,7 +267,7 @@ darwin_current_sos ()
>   	break;
>   
>         /* Create and fill the new struct solib element.  */
> -      auto &newobj = sos.emplace_back (darwin_so_ops);
> +      auto &newobj = sos.emplace_back (*this);
>   
>         auto li = std::make_unique<lm_info_darwin> ();
>   
> @@ -457,10 +474,8 @@ darwin_solib_read_all_image_info_addr (struct darwin_info *info)
>     info->all_image_addr = extract_unsigned_integer (buf, len, BFD_ENDIAN_BIG);
>   }
>   
> -/* Shared library startup support.  See documentation in solib-svr4.c.  */
> -
> -static void
> -darwin_solib_create_inferior_hook (int from_tty)
> +void
> +darwin_solib_ops::create_inferior_hook (int from_tty)
>   {
>     /* Everything below only makes sense if we have a running inferior.  */
>     if (!target_has_execution ())
> @@ -560,8 +575,8 @@ darwin_solib_create_inferior_hook (int from_tty)
>       create_solib_event_breakpoint (current_inferior ()->arch (), notifier);
>   }
>   
> -static void
> -darwin_clear_solib (program_space *pspace)
> +void
> +darwin_solib_ops::clear_solib (program_space *pspace)
>   {
>     darwin_info *info = get_darwin_info (pspace);
>   
> @@ -572,8 +587,9 @@ darwin_clear_solib (program_space *pspace)
>   /* The section table is built from bfd sections using bfd VMAs.
>      Relocate these VMAs according to solib info.  */
>   
> -static void
> -darwin_relocate_section_addresses (solib &so, target_section *sec)
> +void
> +darwin_solib_ops::relocate_section_addresses (solib &so,
> +					      target_section *sec) const
>   {
>     auto *li = gdb::checked_static_cast<lm_info_darwin *> (so.lm_info.get ());
>   
> @@ -592,9 +608,9 @@ darwin_relocate_section_addresses (solib &so, target_section *sec)
>     if (sec->addr < so.addr_low)
>       so.addr_low = sec->addr;
>   }
> -\f
> -static gdb_bfd_ref_ptr
> -darwin_bfd_open (const char *pathname)
> +
> +gdb_bfd_ref_ptr
> +darwin_solib_ops::bfd_open (const char *pathname)
>   {
>     int found_file;
>   
> @@ -622,20 +638,3 @@ darwin_bfd_open (const char *pathname)
>   
>     return res;
>   }
> -
> -const solib_ops darwin_so_ops =
> -{
> -  darwin_relocate_section_addresses,
> -  nullptr,
> -  darwin_clear_solib,
> -  darwin_solib_create_inferior_hook,
> -  darwin_current_sos,
> -  nullptr,
> -  nullptr,
> -  darwin_bfd_open,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  default_find_solib_addr,
> -};
> diff --git a/gdb/solib-darwin.h b/gdb/solib-darwin.h
> index b96e744669fb..09dac6bf52ba 100644
> --- a/gdb/solib-darwin.h
> +++ b/gdb/solib-darwin.h
> @@ -20,8 +20,10 @@
>   #ifndef GDB_SOLIB_DARWIN_H
>   #define GDB_SOLIB_DARWIN_H
>   
> -struct solib_ops;
> +#include "solib.h"
>   
> -extern const solib_ops darwin_so_ops;
> +/* Return a new solib_ops for Darwin systems.  */
> +
> +extern solib_ops_up new_darwin_solib_ops ();
>   
>   #endif /* GDB_SOLIB_DARWIN_H */
> diff --git a/gdb/solib-dsbt.c b/gdb/solib-dsbt.c
> index f6748b69353a..97e32d19e57d 100644
> --- a/gdb/solib-dsbt.c
> +++ b/gdb/solib-dsbt.c
> @@ -120,6 +120,25 @@ struct dbst_ext_link_map
>     ext_ptr l_next, l_prev;	/* struct link_map *l_next, *l_prev; */
>   };
>   
> +/* solib_ops for DSBT systems.  */
> +
> +struct dsbt_solib_ops : public solib_ops
> +{
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  void clear_solib (program_space *pspace) override;
> +  void create_inferior_hook (int from_tty) override;
> +  owning_intrusive_list<solib> current_sos () override;
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> +};
> +
> +/* See solib-dsbt.h.  */
> +
> +solib_ops_up
> +new_dsbt_solib_ops ()
> +{
> +  return std::make_unique<dsbt_solib_ops> ();
> +}
> +
>   /* Link map info to include in an allocated solib entry */
>   
>   struct lm_info_dsbt final : public lm_info
> @@ -502,8 +521,8 @@ lm_base (void)
>      themselves.  The declaration of `struct solib' says which fields
>      we provide values for.  */
>   
> -static owning_intrusive_list<solib>
> -dsbt_current_sos (void)
> +owning_intrusive_list<solib>
> +dsbt_solib_ops::current_sos ()
>   {
>     bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ());
>     CORE_ADDR lm_addr;
> @@ -584,7 +603,7 @@ dsbt_current_sos (void)
>   	      break;
>   	    }
>   
> -	  auto &sop = sos.emplace_back (dsbt_so_ops);
> +	  auto &sop = sos.emplace_back (*this);
>   	  auto li = std::make_unique<lm_info_dsbt> ();
>   	  li->map = loadmap;
>   	  /* Fetch the name.  */
> @@ -623,8 +642,8 @@ dsbt_current_sos (void)
>   /* Return true if PC lies in the dynamic symbol resolution code of the
>      run time loader.  */
>   
> -static bool
> -dsbt_in_dynsym_resolve_code (CORE_ADDR pc)
> +bool
> +dsbt_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
>   {
>     dsbt_info *info = get_dsbt_info (current_program_space);
>   
> @@ -840,8 +859,8 @@ dsbt_relocate_main_executable (void)
>      For the DSBT shared library, the main executable needs to be relocated.
>      The shared library breakpoints also need to be enabled.  */
>   
> -static void
> -dsbt_solib_create_inferior_hook (int from_tty)
> +void
> +dsbt_solib_ops::create_inferior_hook (int from_tty)
>   {
>     /* Relocate main executable.  */
>     dsbt_relocate_main_executable ();
> @@ -854,8 +873,8 @@ dsbt_solib_create_inferior_hook (int from_tty)
>       }
>   }
>   
> -static void
> -dsbt_clear_solib (program_space *pspace)
> +void
> +dsbt_solib_ops::clear_solib (program_space *pspace)
>   {
>     dsbt_info *info = get_dsbt_info (pspace);
>   
> @@ -866,8 +885,9 @@ dsbt_clear_solib (program_space *pspace)
>     info->main_executable_lm_info = NULL;
>   }
>   
> -static void
> -dsbt_relocate_section_addresses (solib &so, target_section *sec)
> +void
> +dsbt_solib_ops::relocate_section_addresses (solib &so,
> +					    target_section *sec) const
>   {
>     int seg;
>     auto *li = gdb::checked_static_cast<lm_info_dsbt *> (so.lm_info.get ());
> @@ -893,23 +913,6 @@ show_dsbt_debug (struct ui_file *file, int from_tty,
>     gdb_printf (file, _("solib-dsbt debugging is %s.\n"), value);
>   }
>   
> -const solib_ops dsbt_so_ops =
> -{
> -  dsbt_relocate_section_addresses,
> -  nullptr,
> -  dsbt_clear_solib,
> -  dsbt_solib_create_inferior_hook,
> -  dsbt_current_sos,
> -  nullptr,
> -  dsbt_in_dynsym_resolve_code,
> -  solib_bfd_open,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  default_find_solib_addr,
> -};
> -
>   void _initialize_dsbt_solib ();
>   void
>   _initialize_dsbt_solib ()
> diff --git a/gdb/solib-dsbt.h b/gdb/solib-dsbt.h
> index d5c52c69e432..47b03bba589b 100644
> --- a/gdb/solib-dsbt.h
> +++ b/gdb/solib-dsbt.h
> @@ -20,8 +20,10 @@
>   #ifndef GDB_SOLIB_DSBT_H
>   #define GDB_SOLIB_DSBT_H
>   
> -struct solib_ops;
> +#include "solib.h"
>   
> -extern const solib_ops dsbt_so_ops;
> +/* Return a new solib_ops for DSBT systems.  */
> +
> +solib_ops_up new_dsbt_solib_ops ();
>   
>   #endif /* GDB_SOLIB_DSBT_H */
> diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c
> index 12d3140b513c..6ef83672577d 100644
> --- a/gdb/solib-frv.c
> +++ b/gdb/solib-frv.c
> @@ -26,6 +26,26 @@
>   #include "elf/frv.h"
>   #include "gdb_bfd.h"
>   #include "inferior.h"
> +#include "solib-frv.h"
> +
> +/* solib_ops for FR-V systems.  */
> +
> +struct frv_solib_ops : public solib_ops
> +{
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  void clear_solib (program_space *pspace) override;
> +  void create_inferior_hook (int from_tty) override;
> +  owning_intrusive_list<solib> current_sos () override;
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> +};
> +
> +/* See solib-frv.h.  */
> +
> +solib_ops_up
> +new_frv_solib_ops ()
> +{
> +  return std::make_unique<frv_solib_ops> ();
> +}
>   
>   /* FR-V pointers are four bytes wide.  */
>   enum { FRV_PTR_SIZE = 4 };
> @@ -293,11 +313,8 @@ lm_base (void)
>     return lm_base_cache;
>   }
>   
> -
> -/* Implement the "current_sos" solib_ops method.  */
> -
> -static owning_intrusive_list<solib>
> -frv_current_sos ()
> +owning_intrusive_list<solib>
> +frv_solib_ops::current_sos ()
>   {
>     bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ());
>     CORE_ADDR lm_addr, mgot;
> @@ -367,7 +384,7 @@ frv_current_sos ()
>   	      break;
>   	    }
>   
> -	  auto &sop = sos.emplace_back (frv_so_ops);
> +	  auto &sop = sos.emplace_back (*this);
>   	  auto li = std::make_unique<lm_info_frv> ();
>   	  li->map = loadmap;
>   	  li->got_value = got_addr;
> @@ -414,8 +431,8 @@ static CORE_ADDR interp_text_sect_high;
>   static CORE_ADDR interp_plt_sect_low;
>   static CORE_ADDR interp_plt_sect_high;
>   
> -static bool
> -frv_in_dynsym_resolve_code (CORE_ADDR pc)
> +bool
> +frv_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
>   {
>     return ((pc >= interp_text_sect_low && pc < interp_text_sect_high)
>   	  || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high)
> @@ -776,8 +793,8 @@ frv_relocate_main_executable (void)
>      to be relocated.  The shared library breakpoints also need to be
>      enabled.  */
>   
> -static void
> -frv_solib_create_inferior_hook (int from_tty)
> +void
> +frv_solib_ops::create_inferior_hook (int from_tty)
>   {
>     /* Relocate main executable.  */
>     frv_relocate_main_executable ();
> @@ -790,8 +807,8 @@ frv_solib_create_inferior_hook (int from_tty)
>       }
>   }
>   
> -static void
> -frv_clear_solib (program_space *pspace)
> +void
> +frv_solib_ops::clear_solib (program_space *pspace)
>   {
>     lm_base_cache = 0;
>     enable_break2_done = 0;
> @@ -801,8 +818,9 @@ frv_clear_solib (program_space *pspace)
>     main_executable_lm_info = NULL;
>   }
>   
> -static void
> -frv_relocate_section_addresses (solib &so, target_section *sec)
> +void
> +frv_solib_ops::relocate_section_addresses (solib &so,
> +					   target_section *sec) const
>   {
>     int seg;
>     auto *li = gdb::checked_static_cast<lm_info_frv *> (so.lm_info.get ());
> @@ -1063,20 +1081,3 @@ frv_fetch_objfile_link_map (struct objfile *objfile)
>     /* Not found!  */
>     return 0;
>   }
> -
> -const solib_ops frv_so_ops =
> -{
> -  frv_relocate_section_addresses,
> -  nullptr,
> -  frv_clear_solib,
> -  frv_solib_create_inferior_hook,
> -  frv_current_sos,
> -  nullptr,
> -  frv_in_dynsym_resolve_code,
> -  solib_bfd_open,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  default_find_solib_addr,
> -};
> diff --git a/gdb/solib-frv.h b/gdb/solib-frv.h
> new file mode 100644
> index 000000000000..0f43b39cd3e5
> --- /dev/null
> +++ b/gdb/solib-frv.h
> @@ -0,0 +1,28 @@
> +/* Handle FR-V (FDPIC) shared libraries for GDB, the GNU Debugger.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GDB_SOLIB_FRV_H
> +#define GDB_SOLIB_FRV_H
> +
> +#include "solib.h"
> +
> +/* Return a new solib_ops for FR-V systems.  */
> +
> +solib_ops_up new_frv_solib_ops ();
> +
> +#endif /* GDB_SOLIB_FRV_H */
> diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
> index a3599562e795..ab7dc3e66808 100644
> --- a/gdb/solib-rocm.c
> +++ b/gdb/solib-rocm.c
> @@ -26,6 +26,7 @@
>   #include "event-top.h"
>   #include "gdbsupport/fileio.h"
>   #include "inferior.h"
> +#include "linux-tdep.h"
>   #include "observable.h"
>   #include "solib.h"
>   #include "solib-svr4.h"
> @@ -153,7 +154,69 @@ struct solib_info
>   /* Per-inferior data key.  */
>   static const registry<inferior>::key<solib_info> rocm_solib_data;
>   
> -static solib_ops rocm_solib_ops;
> +/* solib_ops for ROCm systems.  */
> +
> +struct rocm_solib_ops : public solib_ops
> +{
> +  /* HOST_OPS is the host solib_ops that rocm_solib_ops hijacks / wraps,
> +     in order to provide support for ROCm code objects.  */
> +  explicit rocm_solib_ops (solib_ops_up host_ops)
> +    : m_host_ops (std::move (host_ops))
> +  {
> +  }
> +
> +  /* The methods implemented by rocm_solib_ops.  */
> +  owning_intrusive_list<solib> current_sos () override;
> +  void create_inferior_hook (int from_tty) override;
> +  gdb_bfd_ref_ptr bfd_open (const char *pathname) override;
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  void handle_event () override;
> +
> +  /* Implement the following methods just to forward the calls to the host
> +     solib_ops.  We currently need to implement all the methods that
> +     svr4_solib_ops implements.  */
> +  void clear_so (const solib &so) const override
> +  { return m_host_ops->clear_so (so); }
> +
> +  void clear_solib (program_space *pspace) override
> +  { return m_host_ops->clear_solib (pspace); }
> +
> +  bool open_symbol_file_object (int from_tty) override
> +  { return m_host_ops->open_symbol_file_object (from_tty); }
> +
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override
> +  { return m_host_ops->in_dynsym_resolve_code (pc); }
> +
> +  bool same (const solib &gdb, const solib &inferior) const override
> +  { return m_host_ops->same (gdb, inferior); }
> +
> +  bool keep_data_in_core (CORE_ADDR vaddr, unsigned long size) override
> +  { return m_host_ops->keep_data_in_core (vaddr, size); }
> +
> +  void update_breakpoints () const override
> +  { return m_host_ops->update_breakpoints (); }
> +
> +  std::optional<CORE_ADDR> find_solib_addr (solib &so) const override
> +  { return m_host_ops->find_solib_addr (so); }
> +
> +  bool supports_namespaces () const override
> +  { return true; }
> +
> +  int find_solib_ns (const solib &so) const override
> +  { return m_host_ops->find_solib_ns (so); }
> +
> +  int num_active_namespaces () const override
> +  { return m_host_ops->num_active_namespaces (); }
> +
> +  std::vector<const solib *> get_solibs_in_ns (int nsid) const override
> +  { return m_host_ops->get_solibs_in_ns (nsid); }
> +
> +private:
> +  owning_intrusive_list<solib>
> +  solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
> +
> +  solib_ops_up m_host_ops;
> +};
>   
>   /* Fetch the solib_info data for INF.  */
>   
> @@ -170,13 +233,13 @@ get_solib_info (inferior *inf)
>   
>   /* Relocate section addresses.  */
>   
> -static void
> -rocm_solib_relocate_section_addresses (solib &so,
> -				       struct target_section *sec)
> +void
> +rocm_solib_ops::relocate_section_addresses (solib &so,
> +					    struct target_section *sec) const
>   {
>     if (!is_amdgpu_arch (gdbarch_from_bfd (so.abfd.get ())))
>       {
> -      svr4_so_ops.relocate_section_addresses (so, sec);
> +      m_host_ops->relocate_section_addresses (so, sec);
>         return;
>       }
>   
> @@ -187,30 +250,30 @@ rocm_solib_relocate_section_addresses (solib &so,
>   
>   static void rocm_update_solib_list ();
>   
> -static void
> -rocm_solib_handle_event ()
> +void
> +rocm_solib_ops::handle_event ()
>   {
> -  /* Since we sit on top of svr4_so_ops, we might get called following an event
> -     concerning host libraries.  We must therefore forward the call.  If the
> -     event was for a ROCm code object, it will be a no-op.  On the other hand,
> +  /* Since we sit on top of a host solib_ops, we might get called following an
> +     event concerning host libraries.  We must therefore forward the call.  If
> +     the event was for a ROCm code object, it will be a no-op.  On the other hand
>        if the event was for host libraries, rocm_update_solib_list will be
>        essentially be a no-op (it will reload the same code object list as was
>        previously loaded).  */
> -  svr4_so_ops.handle_event ();
> +  m_host_ops->handle_event ();
>   
>     rocm_update_solib_list ();
>   }
>   
>   /* Create solib objects from rocm_so objects in SOS.  */
>   
> -static owning_intrusive_list<solib>
> -solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
> +owning_intrusive_list<solib>
> +rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
>   {
>     owning_intrusive_list<solib> dst;
>   
>     for (const rocm_so &so : sos)
>       {
> -      auto &newobj = dst.emplace_back (rocm_solib_ops);
> +      auto &newobj = dst.emplace_back (*this);
>   
>         newobj.lm_info = std::make_unique<lm_info_svr4> (*so.lm_info);
>         newobj.name = so.name;
> @@ -223,11 +286,11 @@ solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
>   /* Build a list of `struct solib' objects describing the shared
>      objects currently loaded in the inferior.  */
>   
> -static owning_intrusive_list<solib>
> -rocm_solib_current_sos ()
> +owning_intrusive_list<solib>
> +rocm_solib_ops::current_sos ()
>   {
>     /* First, retrieve the host-side shared library list.  */
> -  owning_intrusive_list<solib> sos = svr4_so_ops.current_sos ();
> +  owning_intrusive_list<solib> sos = m_host_ops->current_sos ();
>   
>     /* Then, the device-side shared library list.  */
>     std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list;
> @@ -579,12 +642,12 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
>       }
>   }
>   
> -static gdb_bfd_ref_ptr
> -rocm_solib_bfd_open (const char *pathname)
> +gdb_bfd_ref_ptr
> +rocm_solib_ops::bfd_open (const char *pathname)
>   {
>     /* Handle regular files with SVR4 open.  */
>     if (strstr (pathname, "://") == nullptr)
> -    return svr4_so_ops.bfd_open (pathname);
> +    return m_host_ops->bfd_open (pathname);
>   
>     auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
>     {
> @@ -668,12 +731,12 @@ rocm_solib_bfd_open (const char *pathname)
>     return abfd;
>   }
>   
> -static void
> -rocm_solib_create_inferior_hook (int from_tty)
> +void
> +rocm_solib_ops::create_inferior_hook (int from_tty)
>   {
>     get_solib_info (current_inferior ())->solib_list.clear ();
>   
> -  svr4_so_ops.solib_create_inferior_hook (from_tty);
> +  m_host_ops->create_inferior_hook (from_tty);
>   }
>   
>   static void
> @@ -736,22 +799,6 @@ rocm_update_solib_list ()
>   
>         sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
>       }
> -
> -  if (rocm_solib_ops.current_sos == NULL)
> -    {
> -      /* Override what we need to.  */
> -      rocm_solib_ops = svr4_so_ops;
> -      rocm_solib_ops.current_sos = rocm_solib_current_sos;
> -      rocm_solib_ops.solib_create_inferior_hook
> -	= rocm_solib_create_inferior_hook;
> -      rocm_solib_ops.bfd_open = rocm_solib_bfd_open;
> -      rocm_solib_ops.relocate_section_addresses
> -	= rocm_solib_relocate_section_addresses;
> -      rocm_solib_ops.handle_event = rocm_solib_handle_event;
> -
> -      /* Engage the ROCm so_ops.  */
> -      set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops);
> -    }
>   }
>   
>   static void
> @@ -759,6 +806,10 @@ rocm_solib_target_inferior_created (inferior *inf)
>   {
>     get_solib_info (inf)->solib_list.clear ();
>   
> +  auto prev_ops = inf->pspace->release_solib_ops ();
> +  auto rocm_ops = std::make_unique<rocm_solib_ops> (std::move (prev_ops));
> +  inf->pspace->set_solib_ops (std::move (rocm_ops));
> +
>     rocm_update_solib_list ();
>   
>     /* Force GDB to reload the solibs.  */
> @@ -766,6 +817,21 @@ rocm_solib_target_inferior_created (inferior *inf)
>     solib_add (nullptr, 0, auto_solib_add);
>   }
>   
> +static void
> +rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf)
> +{
> +  /* Engage the ROCm so_ops, but only if dbgapi is attached to the inferior
> +     (avoiding remote inferiors and core file debugging).  */
> +  if (get_amd_dbgapi_process_id (follow_inf) == AMD_DBGAPI_PROCESS_NONE)
> +    return;
> +
> +  auto prev_ops = follow_inf->pspace->release_solib_ops ();
> +  auto rocm_ops = std::make_unique<rocm_solib_ops> (std::move (prev_ops));
> +  follow_inf->pspace->set_solib_ops (std::move (rocm_ops));
> +
> +  get_solib_info (exec_inf)->solib_list.clear ();
> +}
> +
>   /* -Wmissing-prototypes */
>   extern initialize_file_ftype _initialize_rocm_solib;
>   
> @@ -779,4 +845,8 @@ _initialize_rocm_solib ()
>       (rocm_solib_target_inferior_created,
>        "solib-rocm",
>        { &get_amd_dbgapi_target_inferior_created_observer_token () });
> +
> +  gdb::observers::inferior_execd.attach
> +    (rocm_solib_target_inferior_execd, "solib-rocm",
> +     { &get_amd_dbgapi_target_inferior_execd_observer_token () });
>   }
> diff --git a/gdb/solib-svr4-linux.c b/gdb/solib-svr4-linux.c
> new file mode 100644
> index 000000000000..c4bd8d89cfab
> --- /dev/null
> +++ b/gdb/solib-svr4-linux.c
> @@ -0,0 +1,98 @@
> +/* Target-dependent code for GNU/Linux using SVR4-style libraries.
> +
> +   Copyright (C) 2025 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "solib-svr4-linux.h"
> +
> +/* See solib-svr4-linux.h.  */
> +
> +solib_ops_up
> +new_linux_ilp32_svr4_solib_ops ()
> +{
> +  return std::make_unique<linux_ilp32_svr4_solib_ops> ();
> +}
> +
> +/* See solib-svr4-linux.h.  */
> +
> +link_map_offsets *
> +linux_ilp32_svr4_solib_ops::fetch_link_map_offsets () const
> +{
> +  static link_map_offsets lmo;
> +  static link_map_offsets *lmp = nullptr;
> +
> +  if (lmp == nullptr)
> +    {
> +      lmp = &lmo;
> +
> +      lmo.r_version_offset = 0;
> +      lmo.r_version_size = 4;
> +      lmo.r_map_offset = 4;
> +      lmo.r_brk_offset = 8;
> +      lmo.r_ldsomap_offset = -1;
> +      lmo.r_next_offset = 20;
> +
> +      /* Everything we need is in the first 20 bytes.  */
> +      lmo.link_map_size = 20;
> +      lmo.l_addr_offset = 0;
> +      lmo.l_name_offset = 4;
> +      lmo.l_ld_offset = 8;
> +      lmo.l_next_offset = 12;
> +      lmo.l_prev_offset = 16;
> +    }
> +
> +  return lmp;
> +}
> +
> +/* See solib-svr4-linux.h.  */
> +
> +solib_ops_up
> +new_linux_lp64_svr4_solib_ops ()
> +{
> +  return std::make_unique<linux_lp64_svr4_solib_ops> ();
> +}
> +
> +/* See linux-tdep.h.  */
> +
> +link_map_offsets *
> +linux_lp64_svr4_solib_ops::fetch_link_map_offsets () const
> +{
> +  static link_map_offsets lmo;
> +  static link_map_offsets *lmp = nullptr;
> +
> +  if (lmp == nullptr)
> +    {
> +      lmp = &lmo;
> +
> +      lmo.r_version_offset = 0;
> +      lmo.r_version_size = 4;
> +      lmo.r_map_offset = 8;
> +      lmo.r_brk_offset = 16;
> +      lmo.r_ldsomap_offset = -1;
> +      lmo.r_next_offset = 40;
> +
> +      /* Everything we need is in the first 40 bytes.  */
> +      lmo.link_map_size = 40;
> +      lmo.l_addr_offset = 0;
> +      lmo.l_name_offset = 8;
> +      lmo.l_ld_offset = 16;
> +      lmo.l_next_offset = 24;
> +      lmo.l_prev_offset = 32;
> +    }
> +
> +  return lmp;
> +}
> diff --git a/gdb/solib-svr4-linux.h b/gdb/solib-svr4-linux.h
> new file mode 100644
> index 000000000000..885785fdfcba
> --- /dev/null
> +++ b/gdb/solib-svr4-linux.h
> @@ -0,0 +1,47 @@
> +/* Target-dependent code for GNU/Linux using SVR4-style libraries.
> +
> +   Copyright (C) 2025 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GDB_SOLIB_SVR4_LINUX_H
> +#define GDB_SOLIB_SVR4_LINUX_H
> +
> +#include "solib-svr4.h"
> +
> +/* solib_ops for ILP32 Linux systems.  */
> +
> +struct linux_ilp32_svr4_solib_ops : public svr4_solib_ops
> +{
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
> +/* solib_ops for LP64 Linux systems.  */
> +
> +struct linux_lp64_svr4_solib_ops : public svr4_solib_ops
> +{
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
> +/* Return a new solib_ops for ILP32 Linux systems.  */
> +
> +extern solib_ops_up new_linux_ilp32_svr4_solib_ops ();
> +
> +/* Return a new solib_ops for LP64 Linux systems.  */
> +
> +extern solib_ops_up new_linux_lp64_svr4_solib_ops ();
> +
> +#endif /* GDB_SOLIB_SVR4_LINUX_H */
> diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
> index deefc2578599..e9430281bca0 100644
> --- a/gdb/solib-svr4.c
> +++ b/gdb/solib-svr4.c
> @@ -47,7 +47,6 @@
>   
>   #include <map>
>   
> -static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
>   static void svr4_relocate_main_executable (void);
>   static void probes_table_remove_objfile_probes (struct objfile *objfile);
>   static void svr4_iterate_over_objfiles_in_search_order
> @@ -89,27 +88,6 @@ static const  char * const main_name_list[] =
>     NULL
>   };
>   
> -/* What to do when a probe stop occurs.  */
> -
> -enum probe_action
> -{
> -  /* Something went seriously wrong.  Stop using probes and
> -     revert to using the older interface.  */
> -  PROBES_INTERFACE_FAILED,
> -
> -  /* No action is required.  The shared object list is still
> -     valid.  */
> -  DO_NOTHING,
> -
> -  /* The shared object list should be reloaded entirely.  */
> -  FULL_RELOAD,
> -
> -  /* Attempt to incrementally update the shared object list. If
> -     the update fails or is not possible, fall back to reloading
> -     the list in full.  */
> -  UPDATE_OR_RELOAD,
> -};
> -
>   /* A probe's name and its associated action.  */
>   
>   struct probe_info
> @@ -184,8 +162,8 @@ svr4_same (const char *gdb_name, const char *inferior_name,
>     return gdb_lm_info.l_addr_inferior == inferior_lm_info.l_addr_inferior;
>   }
>   
> -static int
> -svr4_same (const solib &gdb, const solib &inferior)
> +bool
> +svr4_solib_ops::same (const solib &gdb, const solib &inferior) const
>   {
>     auto *lmg
>       = gdb::checked_static_cast<const lm_info_svr4 *> (gdb.lm_info.get ());
> @@ -196,10 +174,10 @@ svr4_same (const solib &gdb, const solib &inferior)
>   		    inferior.original_name.c_str (), *lmg, *lmi);
>   }
>   
> -static lm_info_svr4_up
> -lm_info_read (CORE_ADDR lm_addr)
> +lm_info_svr4_up
> +svr4_solib_ops::read_lm_info (CORE_ADDR lm_addr) const
>   {
> -  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>     lm_info_svr4_up lm_info;
>   
>     gdb::byte_vector lm (lmo->link_map_size);
> @@ -229,16 +207,16 @@ lm_info_read (CORE_ADDR lm_addr)
>     return lm_info;
>   }
>   
> -static int
> -has_lm_dynamic_from_link_map (void)
> +int
> +svr4_solib_ops::has_lm_dynamic_from_link_map () const
>   {
> -  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>   
>     return lmo->l_ld_offset >= 0;
>   }
>   
> -static CORE_ADDR
> -lm_addr_check (const solib &so, bfd *abfd)
> +CORE_ADDR
> +svr4_solib_ops::lm_addr_check (const solib &so, bfd *abfd) const
>   {
>     auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
>   
> @@ -249,7 +227,7 @@ lm_addr_check (const solib &so, bfd *abfd)
>   
>         l_addr = li->l_addr_inferior;
>   
> -      if (! abfd || ! has_lm_dynamic_from_link_map ())
> +      if (!abfd || !this->has_lm_dynamic_from_link_map ())
>   	goto set_addr;
>   
>         l_dynaddr = li->l_ld;
> @@ -475,8 +453,8 @@ svr4_is_default_namespace (const svr4_info *info, CORE_ADDR debug_base)
>   
>   /* Free the probes table.  */
>   
> -static void
> -free_probes_table (struct svr4_info *info)
> +void
> +svr4_solib_ops::free_probes_table (svr4_info *info) const
>   {
>     info->probes_table.reset (nullptr);
>   }
> @@ -820,10 +798,10 @@ elf_locate_base (void)
>      checking r_version for a known version number, or r_state for
>      RT_CONSISTENT.  */
>   
> -static CORE_ADDR
> -solib_svr4_r_map (CORE_ADDR debug_base)
> +CORE_ADDR
> +svr4_solib_ops::read_r_map (CORE_ADDR debug_base) const
>   {
> -  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>     type *ptr_type
>       = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
>     CORE_ADDR addr = 0;
> @@ -843,10 +821,10 @@ solib_svr4_r_map (CORE_ADDR debug_base)
>   
>   /* Find r_brk from the inferior's debug base.  */
>   
> -static CORE_ADDR
> -solib_svr4_r_brk (struct svr4_info *info)
> +CORE_ADDR
> +svr4_solib_ops::find_r_brk (svr4_info *info) const
>   {
> -  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>     type *ptr_type
>       = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
>   
> @@ -857,10 +835,10 @@ solib_svr4_r_brk (struct svr4_info *info)
>   /* Find the link map for the dynamic linker (if it is not in the
>      normal list of loaded shared objects).  */
>   
> -static CORE_ADDR
> -solib_svr4_r_ldsomap (struct svr4_info *info)
> +CORE_ADDR
> +svr4_solib_ops::find_r_ldsomap (svr4_info *info) const
>   {
> -  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>     type *ptr_type
>       = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
>     enum bfd_endian byte_order = type_byte_order (ptr_type);
> @@ -888,10 +866,10 @@ solib_svr4_r_ldsomap (struct svr4_info *info)
>   
>   /* Find the next namespace from the r_next field.  */
>   
> -static CORE_ADDR
> -solib_svr4_r_next (CORE_ADDR debug_base)
> +CORE_ADDR
> +svr4_solib_ops::read_r_next (CORE_ADDR debug_base) const
>   {
> -  link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>     type *ptr_type
>       = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
>     bfd_endian byte_order = type_byte_order (ptr_type);
> @@ -923,8 +901,8 @@ solib_svr4_r_next (CORE_ADDR debug_base)
>      memory areas containing the l_name string are saved in the core
>      file.  */
>   
> -static int
> -svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
> +bool
> +svr4_solib_ops::keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
>   {
>     struct svr4_info *info;
>     CORE_ADDR ldsomap;
> @@ -934,13 +912,13 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
>   
>     info->debug_base = elf_locate_base ();
>     if (info->debug_base == 0)
> -    return 0;
> +    return false;
>   
> -  ldsomap = solib_svr4_r_ldsomap (info);
> +  ldsomap = this->find_r_ldsomap (info);
>     if (!ldsomap)
> -    return 0;
> +    return false;
>   
> -  std::unique_ptr<lm_info_svr4> li = lm_info_read (ldsomap);
> +  std::unique_ptr<lm_info_svr4> li = this->read_lm_info (ldsomap);
>     name_lm = li != NULL ? li->l_name : 0;
>   
>     return (name_lm >= vaddr && name_lm < vaddr + size);
> @@ -948,11 +926,11 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
>   
>   /* See solib.h.  */
>   
> -static int
> -open_symbol_file_object (int from_tty)
> +bool
> +svr4_solib_ops::open_symbol_file_object (int from_tty)
>   {
>     CORE_ADDR lm, l_name;
> -  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
> +  link_map_offsets *lmo = this->fetch_link_map_offsets ();
>     type *ptr_type
>       = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
>     int l_name_size = ptr_type->length ();
> @@ -965,17 +943,17 @@ open_symbol_file_object (int from_tty)
>   
>     if (current_program_space->symfile_object_file)
>       if (!query (_("Attempt to reload symbols from process? ")))
> -      return 0;
> +      return false;
>   
>     /* Always locate the debug struct, in case it has moved.  */
>     info->debug_base = elf_locate_base ();
>     if (info->debug_base == 0)
> -    return 0;	/* failed somehow...  */
> +    return false;	/* failed somehow...  */
>   
>     /* First link map member should be the executable.  */
> -  lm = solib_svr4_r_map (info->debug_base);
> +  lm = this->read_r_map (info->debug_base);
>     if (lm == 0)
> -    return 0;	/* failed somehow...  */
> +    return false;	/* failed somehow...  */
>   
>     /* Read address of name from target memory to GDB.  */
>     read_memory (lm + lmo->l_name_offset, l_name_buf.data (), l_name_size);
> @@ -984,7 +962,7 @@ open_symbol_file_object (int from_tty)
>     l_name = extract_typed_address (l_name_buf.data (), ptr_type);
>   
>     if (l_name == 0)
> -    return 0;		/* No filename.  */
> +    return false;		/* No filename.  */
>   
>     /* Now fetch the filename from target memory.  */
>     gdb::unique_xmalloc_ptr<char> filename
> @@ -993,13 +971,13 @@ open_symbol_file_object (int from_tty)
>     if (filename == nullptr)
>       {
>         warning (_("failed to read exec filename from attached file"));
> -      return 0;
> +      return false;
>       }
>   
>     /* Have a pathname: read the symbol file.  */
>     symbol_file_add_main (filename.get (), add_flags);
>   
> -  return 1;
> +  return true;
>   }
>   
>   /* Data exchange structure for the XML parser as returned by
> @@ -1032,8 +1010,8 @@ svr4_free_objfile_observer (struct objfile *objfile)
>   
>   /* Implement solib_ops.clear_so.  */
>   
> -static void
> -svr4_clear_so (const solib &so)
> +void
> +svr4_solib_ops::clear_so (const solib &so) const
>   {
>     auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
>   
> @@ -1043,14 +1021,14 @@ svr4_clear_so (const solib &so)
>   
>   /* Create the solib objects equivalent to the svr4_sos in SOS.  */
>   
> -static owning_intrusive_list<solib>
> -solib_from_svr4_sos (const std::vector<svr4_so> &sos)
> +owning_intrusive_list<solib>
> +svr4_solib_ops::solibs_from_svr4_sos (const std::vector<svr4_so> &sos)
>   {
>     owning_intrusive_list<solib> dst;
>   
>     for (const svr4_so &so : sos)
>       {
> -      auto &newobj = dst.emplace_back (svr4_so_ops);
> +      auto &newobj = dst.emplace_back (*this);
>   
>         newobj.name = so.name;
>         newobj.original_name = so.name;
> @@ -1237,8 +1215,8 @@ svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list,
>   /* If no shared library information is available from the dynamic
>      linker, build a fallback list from other sources.  */
>   
> -static owning_intrusive_list<solib>
> -svr4_default_sos (svr4_info *info)
> +owning_intrusive_list<solib>
> +svr4_solib_ops::default_sos (svr4_info *info)
>   {
>     if (!info->debug_loader_offset_p)
>       return {};
> @@ -1250,7 +1228,7 @@ svr4_default_sos (svr4_info *info)
>     li->l_addr_p = 1;
>   
>     owning_intrusive_list<solib> sos;
> -  auto &newobj = sos.emplace_back (svr4_so_ops);
> +  auto &newobj = sos.emplace_back (*this);
>   
>     newobj.lm_info = std::move (li);
>     newobj.name = info->debug_loader_name;
> @@ -1266,16 +1244,16 @@ svr4_default_sos (svr4_info *info)
>      is returned the entries stored to LINK_PTR_PTR are still valid although they may
>      represent only part of the inferior library list.  */
>   
> -static int
> -svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
> -		   std::vector<svr4_so> &sos, int ignore_first)
> +int
> +svr4_solib_ops::read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
> +			      std::vector<svr4_so> &sos, int ignore_first) const
>   {
>     CORE_ADDR first_l_name = 0;
>     CORE_ADDR next_lm;
>   
>     for (; lm != 0; prev_lm = lm, lm = next_lm)
>       {
> -      lm_info_svr4_up li = lm_info_read (lm);
> +      lm_info_svr4_up li = this->read_lm_info (lm);
>         if (li == NULL)
>   	return 0;
>   
> @@ -1331,8 +1309,8 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
>      stored by the probes interface.  Handle special cases relating
>      to the first elements of the list in default namespace.  */
>   
> -static void
> -svr4_current_sos_direct (struct svr4_info *info)
> +void
> +svr4_solib_ops::current_sos_direct (svr4_info *info) const
>   {
>     CORE_ADDR lm;
>     bool ignore_first;
> @@ -1398,15 +1376,15 @@ svr4_current_sos_direct (struct svr4_info *info)
>     /* Collect the sos in each namespace.  */
>     CORE_ADDR debug_base = info->debug_base;
>     for (; debug_base != 0;
> -       ignore_first = false, debug_base = solib_svr4_r_next (debug_base))
> +       ignore_first = false, debug_base = this->read_r_next (debug_base))
>       {
>         /* Walk the inferior's link map list, and build our so_list list.  */
> -      lm = solib_svr4_r_map (debug_base);
> +      lm = this->read_r_map (debug_base);
>         if (lm != 0)
>   	{
>   	  svr4_maybe_add_namespace (info, debug_base);
> -	  svr4_read_so_list (info, lm, 0, info->solib_lists[debug_base],
> -			     ignore_first);
> +	  this->read_so_list (info, lm, 0, info->solib_lists[debug_base],
> +			      ignore_first);
>   	}
>       }
>   
> @@ -1419,15 +1397,15 @@ svr4_current_sos_direct (struct svr4_info *info)
>        r_debug object.  If we added it to the default namespace (as it was),
>        we would probably run into inconsistencies with the load map's
>        prev/next links (I wonder if we did).  */
> -  debug_base = solib_svr4_r_ldsomap (info);
> +  debug_base = this->find_r_ldsomap (info);
>     if (debug_base != 0)
>       {
>         /* Add the dynamic linker's namespace unless we already did.  */
>         if (info->solib_lists.find (debug_base) == info->solib_lists.end ())
>   	{
>   	  svr4_maybe_add_namespace (info, debug_base);
> -	  svr4_read_so_list (info, debug_base, 0, info->solib_lists[debug_base],
> -			     0);
> +	  this->read_so_list (info, debug_base, 0,
> +			      info->solib_lists[debug_base], 0);
>   	}
>       }
>   
> @@ -1436,15 +1414,15 @@ svr4_current_sos_direct (struct svr4_info *info)
>   
>   /* Collect sos read and stored by the probes interface.  */
>   
> -static owning_intrusive_list<solib>
> -svr4_collect_probes_sos (svr4_info *info)
> +owning_intrusive_list<solib>
> +svr4_solib_ops::collect_probes_sos (svr4_info *info)
>   {
>     owning_intrusive_list<solib> res;
>   
>     for (const auto &tuple : info->solib_lists)
>       {
>         const std::vector<svr4_so> &sos = tuple.second;
> -      res.splice (solib_from_svr4_sos (sos));
> +      res.splice (this->solibs_from_svr4_sos (sos));
>       }
>   
>     return res;
> @@ -1453,26 +1431,26 @@ svr4_collect_probes_sos (svr4_info *info)
>   /* Implement the main part of the "current_sos" solib_ops
>      method.  */
>   
> -static owning_intrusive_list<solib>
> -svr4_current_sos_1 (svr4_info *info)
> +owning_intrusive_list<solib>
> +svr4_solib_ops::current_sos_1 (svr4_info *info)
>   {
>     owning_intrusive_list<solib> sos;
>   
>     /* If we're using the probes interface, we can use the cache as it will
>        be maintained by probe update/reload actions.  */
>     if (info->probes_table != nullptr)
> -    sos = svr4_collect_probes_sos (info);
> +    sos = this->collect_probes_sos (info);
>   
>     /* If we're not using the probes interface or if we didn't cache
>        anything, read the sos to fill the cache, then collect them from the
>        cache.  */
>     if (sos.empty ())
>       {
> -      svr4_current_sos_direct (info);
> +      this->current_sos_direct (info);
>   
> -      sos = svr4_collect_probes_sos (info);
> +      sos = this->collect_probes_sos (info);
>         if (sos.empty ())
> -	sos = svr4_default_sos (info);
> +	sos = this->default_sos (info);
>       }
>   
>     return sos;
> @@ -1480,11 +1458,11 @@ svr4_current_sos_1 (svr4_info *info)
>   
>   /* Implement the "current_sos" solib_ops method.  */
>   
> -static owning_intrusive_list<solib>
> -svr4_current_sos ()
> +owning_intrusive_list<solib>
> +svr4_solib_ops::current_sos ()
>   {
>     svr4_info *info = get_svr4_info (current_program_space);
> -  owning_intrusive_list<solib> sos = svr4_current_sos_1 (info);
> +  owning_intrusive_list<solib> sos = this->current_sos_1 (info);
>     struct mem_range vsyscall_range;
>   
>     /* Filter out the vDSO module, if present.  Its symbol file would
> @@ -1731,7 +1709,11 @@ tls_maybe_fill_slot (solib &so)
>       {
>         /* Cause svr4_current_sos() to be run if it hasn't been already.  */
>         if (info->main_lm_addr == 0)
> -	svr4_current_sos_direct (info);
> +	{
> +	  auto &ops
> +	    = gdb::checked_static_cast<svr4_solib_ops &> (so.ops ());
> +	  ops.current_sos_direct (info);
> +	}
>   
>         /* Quit early when main_lm_addr is still 0.  */
>         if (info->main_lm_addr == 0)
> @@ -1802,7 +1784,7 @@ match_main (const char *soname)
>      SVR4 run time loader.  */
>   
>   bool
> -svr4_in_dynsym_resolve_code (CORE_ADDR pc)
> +svr4_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
>   {
>     struct svr4_info *info = get_svr4_info (current_program_space);
>   
> @@ -2001,12 +1983,10 @@ solib_event_probe_action (struct probe_and_action *pa)
>      shared objects from the inferior.  Handle special cases relating
>      to the first elements of the list.  Returns nonzero on success.  */
>   
> -static int
> -solist_update_full (struct svr4_info *info)
> +void
> +svr4_solib_ops::update_full (svr4_info *info) const
>   {
> -  svr4_current_sos_direct (info);
> -
> -  return 1;
> +  this->current_sos_direct (info);
>   }
>   
>   /* Update the shared object list starting from the link-map entry
> @@ -2014,9 +1994,9 @@ solist_update_full (struct svr4_info *info)
>      nonzero if the list was successfully updated, or zero to indicate
>      failure.  */
>   
> -static int
> -solist_update_incremental (svr4_info *info, CORE_ADDR debug_base,
> -			   CORE_ADDR lm)
> +int
> +svr4_solib_ops::update_incremental (svr4_info *info, CORE_ADDR debug_base,
> +				    CORE_ADDR lm) const
>   {
>     /* Fall back to a full update if we are using a remote target
>        that does not support incremental transfers.  */
> @@ -2094,7 +2074,7 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base,
>   	 above check and deferral to solist_update_full ensures
>   	 that this call to svr4_read_so_list will never see the
>   	 first element.  */
> -      if (!svr4_read_so_list (info, lm, prev_lm, solist, 0))
> +      if (!this->read_so_list (info, lm, prev_lm, solist, 0))
>   	return 0;
>       }
>   
> @@ -2105,8 +2085,8 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base,
>      original interface.  We don't reset the breakpoints as the
>      ones set up for the probes-based interface are adequate.  */
>   
> -static void
> -disable_probes_interface (svr4_info *info)
> +void
> +svr4_solib_ops::disable_probes_interface (svr4_info *info) const
>   {
>     warning (_("Probes-based dynamic linker interface failed.\n"
>   	     "Reverting to original interface."));
> @@ -2121,8 +2101,8 @@ disable_probes_interface (svr4_info *info)
>      probes-based linker interface.  Do nothing if using the
>      standard interface.  */
>   
> -static void
> -svr4_handle_solib_event (void)
> +void
> +svr4_solib_ops::handle_event ()
>   {
>     struct svr4_info *info = get_svr4_info (current_program_space);
>     struct probe_and_action *pa;
> @@ -2147,9 +2127,9 @@ svr4_handle_solib_event (void)
>   
>     /* If anything goes wrong we revert to the original linker
>        interface.  */
> -  auto cleanup = make_scope_exit ([info] ()
> +  auto cleanup = make_scope_exit ([this, info] ()
>       {
> -      disable_probes_interface (info);
> +      this->disable_probes_interface (info);
>       });
>   
>     action = solib_event_probe_action (pa);
> @@ -2251,15 +2231,12 @@ svr4_handle_solib_event (void)
>   
>     if (action == UPDATE_OR_RELOAD)
>       {
> -      if (!solist_update_incremental (info, debug_base, lm))
> +      if (!this->update_incremental (info, debug_base, lm))
>   	action = FULL_RELOAD;
>       }
>   
>     if (action == FULL_RELOAD)
> -    {
> -      if (!solist_update_full (info))
> -	return;
> -    }
> +    this->update_full (info);
>   
>     cleanup.release ();
>   }
> @@ -2306,8 +2283,8 @@ svr4_update_solib_event_breakpoint (struct breakpoint *b)
>   /* Enable or disable optional solib event breakpoints as appropriate.
>      Called whenever stop_on_solib_events is changed.  */
>   
> -static void
> -svr4_update_solib_event_breakpoints (void)
> +void
> +svr4_solib_ops::update_breakpoints () const
>   {
>     for (breakpoint &bp : all_breakpoints_safe ())
>       svr4_update_solib_event_breakpoint (&bp);
> @@ -2318,10 +2295,10 @@ svr4_update_solib_event_breakpoints (void)
>      solib event breakpoint will be created and registered for each
>      probe.  */
>   
> -static void
> -svr4_create_probe_breakpoints (svr4_info *info, struct gdbarch *gdbarch,
> -			       const std::vector<probe *> *probes,
> -			       struct objfile *objfile)
> +void
> +svr4_solib_ops::create_probe_breakpoints (svr4_info *info, gdbarch *gdbarch,
> +					  const std::vector<probe *> *probes,
> +					  objfile *objfile) const
>   {
>     for (int i = 0; i < NUM_PROBES; i++)
>       {
> @@ -2339,17 +2316,17 @@ svr4_create_probe_breakpoints (svr4_info *info, struct gdbarch *gdbarch,
>   	}
>       }
>   
> -  svr4_update_solib_event_breakpoints ();
> +  this->update_breakpoints ();
>   }
>   
>   /* Find all the glibc named probes.  Only if all of the probes are found, then
>      create them and return true.  Otherwise return false.  If WITH_PREFIX is set
>      then add "rtld" to the front of the probe names.  */
> -static bool
> -svr4_find_and_create_probe_breakpoints (svr4_info *info,
> -					struct gdbarch *gdbarch,
> -					struct obj_section *os,
> -					bool with_prefix)
> +bool
> +svr4_solib_ops::find_and_create_probe_breakpoints (svr4_info *info,
> +						   gdbarch *gdbarch,
> +						   obj_section *os,
> +						   bool with_prefix) const
>   {
>     SOLIB_SCOPED_DEBUG_START_END ("objfile=%s, with_prefix=%d",
>   				os->objfile->original_name, with_prefix);
> @@ -2427,7 +2404,7 @@ svr4_find_and_create_probe_breakpoints (svr4_info *info,
>   
>     /* All probes found.  Now create them.  */
>     solib_debug_printf ("using probes interface");
> -  svr4_create_probe_breakpoints (info, gdbarch, probes, os->objfile);
> +  this->create_probe_breakpoints (info, gdbarch, probes, os->objfile);
>     return true;
>   }
>   
> @@ -2443,15 +2420,16 @@ svr4_find_and_create_probe_breakpoints (svr4_info *info,
>      probes aren't found, a single breakpoint is set on the original
>      marker function.  */
>   
> -static void
> -svr4_create_solib_event_breakpoints (svr4_info *info, struct gdbarch *gdbarch,
> -				     CORE_ADDR address)
> +void
> +svr4_solib_ops::create_event_breakpoints (svr4_info *info, gdbarch *gdbarch,
> +					  CORE_ADDR address) const
>   {
>     struct obj_section *os = find_pc_section (address);
>   
>     if (os == nullptr
> -      || (!svr4_find_and_create_probe_breakpoints (info, gdbarch, os, false)
> -	  && !svr4_find_and_create_probe_breakpoints (info, gdbarch, os, true)))
> +      || (!this->find_and_create_probe_breakpoints (info, gdbarch, os, false)
> +	  && !this->find_and_create_probe_breakpoints (info, gdbarch, os,
> +						       true)))
>       {
>         solib_debug_printf ("falling back to r_brk breakpoint: addr=%s",
>   			  paddress (gdbarch, address));
> @@ -2491,8 +2469,8 @@ svr4_create_solib_event_breakpoints (svr4_info *info, struct gdbarch *gdbarch,
>      depending upon whether or not the library is being mapped or unmapped,
>      and then set to RT_CONSISTENT after the library is mapped/unmapped.  */
>   
> -static int
> -enable_break (struct svr4_info *info, int from_tty)
> +int
> +svr4_solib_ops::enable_break (svr4_info *info, int from_tty) const
>   {
>     const char * const *bkpt_namep;
>     asection *interp_sect;
> @@ -2508,8 +2486,8 @@ enable_break (struct svr4_info *info, int from_tty)
>   
>     solib_add (NULL, from_tty, auto_solib_add);
>     sym_addr = 0;
> -  if (info->debug_base && solib_svr4_r_map (info->debug_base) != 0)
> -    sym_addr = solib_svr4_r_brk (info);
> +  if (info->debug_base && this->read_r_map (info->debug_base) != 0)
> +    sym_addr = this->find_r_brk (info);
>   
>     if (sym_addr != 0)
>       {
> @@ -2568,8 +2546,8 @@ enable_break (struct svr4_info *info, int from_tty)
>   		= info->interp_plt_sect_low + bfd_section_size (interp_sect);
>   	    }
>   
> -	  svr4_create_solib_event_breakpoints
> -	    (info, current_inferior ()->arch (), sym_addr);
> +	  this->create_event_breakpoints (info, current_inferior ()->arch (),
> +					  sym_addr);
>   	  return 1;
>   	}
>       }
> @@ -2621,7 +2599,7 @@ enable_break (struct svr4_info *info, int from_tty)
>   	    {
>   	      load_addr_found = 1;
>   	      loader_found_in_list = 1;
> -	      load_addr = lm_addr_check (so, tmp_bfd.get ());
> +	      load_addr = this->lm_addr_check (so, tmp_bfd.get ());
>   	      break;
>   	    }
>   	}
> @@ -2728,9 +2706,8 @@ enable_break (struct svr4_info *info, int from_tty)
>   
>         if (sym_addr != 0)
>   	{
> -	  svr4_create_solib_event_breakpoints (info,
> -					       current_inferior ()->arch (),
> -					       load_addr + sym_addr);
> +	  this->create_event_breakpoints (info, current_inferior ()->arch (),
> +					  load_addr + sym_addr);
>   	  return 1;
>   	}
>   
> @@ -2757,9 +2734,8 @@ enable_break (struct svr4_info *info, int from_tty)
>   	  sym_addr = gdbarch_convert_from_func_ptr_addr
>   	    (current_inferior ()->arch (), sym_addr,
>   	     current_inferior ()->top_target ());
> -	  svr4_create_solib_event_breakpoints (info,
> -					       current_inferior ()->arch (),
> -					       sym_addr);
> +	  this->create_event_breakpoints (info, current_inferior ()->arch (),
> +					  sym_addr);
>   	  return 1;
>   	}
>       }
> @@ -2777,8 +2753,9 @@ enable_break (struct svr4_info *info, int from_tty)
>   	      sym_addr = gdbarch_convert_from_func_ptr_addr
>   		(current_inferior ()->arch (), sym_addr,
>   		 current_inferior ()->top_target ());
> -	      svr4_create_solib_event_breakpoints
> -		(info, current_inferior ()->arch (), sym_addr);
> +	      this->create_event_breakpoints (info,
> +					      current_inferior ()->arch (),
> +					      sym_addr);
>   	      return 1;
>   	    }
>   	}
> @@ -3302,15 +3279,15 @@ svr4_relocate_main_executable (void)
>      addresses, and saving sufficient information about them to allow
>      their symbols to be read at a later time.  */
>   
> -static void
> -svr4_solib_create_inferior_hook (int from_tty)
> +void
> +svr4_solib_ops::create_inferior_hook (int from_tty)
>   {
>     struct svr4_info *info;
>   
>     info = get_svr4_info (current_program_space);
>   
>     /* Clear the probes-based interface's state.  */
> -  free_probes_table (info);
> +  this->free_probes_table (info);
>     info->solib_lists.clear ();
>     info->namespace_id.clear ();
>     info->active_namespaces.clear ();
> @@ -3323,12 +3300,12 @@ svr4_solib_create_inferior_hook (int from_tty)
>     if (!target_has_execution ())
>       return;
>   
> -  if (!enable_break (info, from_tty))
> +  if (!this->enable_break (info, from_tty))
>       return;
>   }
>   
> -static void
> -svr4_clear_solib (program_space *pspace)
> +void
> +svr4_solib_ops::clear_solib (program_space *pspace)
>   {
>     svr4_info *info = get_svr4_info (pspace);
>     info->debug_base = 0;
> @@ -3391,15 +3368,15 @@ find_loadable_elf_internal_phdr (bfd *abfd, bfd_section *asect)
>     return nullptr;
>   }
>   
> -/* Implement solib_ops::relocate_section_addresses() for svr4 targets.  */
> -
> -static void
> -svr4_relocate_section_addresses (solib &so, target_section *sec)
> +void
> +svr4_solib_ops::relocate_section_addresses (solib &so,
> +					    target_section *sec) const
>   {
>     bfd *abfd = sec->the_bfd_section->owner;
>   
> -  sec->addr = svr4_truncate_ptr (sec->addr + lm_addr_check (so, abfd));
> -  sec->endaddr = svr4_truncate_ptr (sec->endaddr + lm_addr_check (so, abfd));
> +  sec->addr = svr4_truncate_ptr (sec->addr + this->lm_addr_check (so, abfd));
> +  sec->endaddr
> +    = svr4_truncate_ptr (sec->endaddr + this->lm_addr_check (so, abfd));
>   
>     struct bfd_section *asect = sec->the_bfd_section;
>     gdb_assert (asect != nullptr);
> @@ -3469,56 +3446,23 @@ svr4_relocate_section_addresses (solib &so, target_section *sec)
>   	}
>       }
>   }
> -\f
>   
> -/* Architecture-specific operations.  */
> -
> -struct solib_svr4_ops
> -{
> -  /* Return a description of the layout of `struct link_map'.  */
> -  struct link_map_offsets *(*fetch_link_map_offsets)(void) = nullptr;
> -};
> -
> -/* Per-architecture data key.  */
> -static const registry<gdbarch>::key<struct solib_svr4_ops> solib_svr4_data;
> -
> -/* Return a default for the architecture-specific operations.  */
> -
> -static struct solib_svr4_ops *
> -get_ops (struct gdbarch *gdbarch)
> -{
> -  struct solib_svr4_ops *ops = solib_svr4_data.get (gdbarch);
> -  if (ops == nullptr)
> -    ops = solib_svr4_data.emplace (gdbarch);
> -  return ops;
> -}
> -
> -/* Set the architecture-specific `struct link_map_offsets' fetcher for
> -   GDBARCH to FLMO.  Also, install SVR4 solib_ops into GDBARCH.  */
> +/* See solib-svr4.h.  */
>   
>   void
> -set_solib_svr4_fetch_link_map_offsets (struct gdbarch *gdbarch,
> -				       struct link_map_offsets *(*flmo) (void))
> +set_solib_svr4_ops (gdbarch *gdbarch, gdbarch_new_solib_ops_ftype new_solib_ops)
>   {
> -  struct solib_svr4_ops *ops = get_ops (gdbarch);
> -
> -  ops->fetch_link_map_offsets = flmo;
> -
> -  set_gdbarch_so_ops (gdbarch, &svr4_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_solib_ops);
>     set_gdbarch_iterate_over_objfiles_in_search_order
>       (gdbarch, svr4_iterate_over_objfiles_in_search_order);
>   }
>   
> -/* Fetch a link_map_offsets structure using the architecture-specific
> -   `struct link_map_offsets' fetcher.  */
> +/* See solib-svr4.h.  */
>   
> -static struct link_map_offsets *
> -svr4_fetch_link_map_offsets (void)
> +solib_ops_up
> +new_svr4_ilp32_solib_ops ()
>   {
> -  struct solib_svr4_ops *ops = get_ops (current_inferior ()->arch ());
> -
> -  gdb_assert (ops->fetch_link_map_offsets);
> -  return ops->fetch_link_map_offsets ();
> +  return std::make_unique<ilp32_svr4_solib_ops> ();
>   }
>   
>   /* Most OS'es that have SVR4-style ELF dynamic libraries define a
> @@ -3528,8 +3472,8 @@ svr4_fetch_link_map_offsets (void)
>   /* Fetch (and possibly build) an appropriate `struct link_map_offsets'
>      for an ILP32 SVR4 system.  */
>   
> -struct link_map_offsets *
> -svr4_ilp32_fetch_link_map_offsets (void)
> +link_map_offsets *
> +ilp32_svr4_solib_ops::fetch_link_map_offsets () const
>   {
>     static struct link_map_offsets lmo;
>     static struct link_map_offsets *lmp = NULL;
> @@ -3557,11 +3501,26 @@ svr4_ilp32_fetch_link_map_offsets (void)
>     return lmp;
>   }
>   
> +/* solib_ops for LP64 SVR4 systems.  */
> +
> +struct lp64_svr4_solib_ops : public svr4_solib_ops
> +{
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
> +/* See solib-svr4.h.  */
> +
> +solib_ops_up
> +new_svr4_lp64_solib_ops ()
> +{
> +  return std::make_unique<lp64_svr4_solib_ops> ();
> +}
> +
>   /* Fetch (and possibly build) an appropriate `struct link_map_offsets'
>      for an LP64 SVR4 system.  */
>   
> -struct link_map_offsets *
> -svr4_lp64_fetch_link_map_offsets (void)
> +link_map_offsets *
> +lp64_svr4_solib_ops::fetch_link_map_offsets () const
>   {
>     static struct link_map_offsets lmo;
>     static struct link_map_offsets *lmp = NULL;
> @@ -3708,19 +3667,15 @@ svr4_iterate_over_objfiles_in_search_order
>       }
>   }
>   
> -/* See solib_ops::find_solib_addr in solist.h.  */
> -
> -static std::optional<CORE_ADDR>
> -svr4_find_solib_addr (solib &so)
> +std::optional<CORE_ADDR>
> +svr4_solib_ops::find_solib_addr (solib &so) const
>   {
>     auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
>     return li->l_addr_inferior;
>   }
>   
> -/* See solib_ops::find_solib_ns in solist.h.  */
> -
> -static int
> -svr4_find_solib_ns (const solib &so)
> +int
> +svr4_solib_ops::find_solib_ns (const solib &so) const
>   {
>     CORE_ADDR debug_base = find_debug_base_for_solib (&so);
>     svr4_info *info = get_svr4_info (current_program_space);
> @@ -3735,17 +3690,15 @@ svr4_find_solib_ns (const solib &so)
>     error (_("No namespace found"));
>   }
>   
> -/* see solib_ops::num_active_namespaces in solist.h.  */
> -static int
> -svr4_num_active_namespaces ()
> +int
> +svr4_solib_ops::num_active_namespaces () const
>   {
>     svr4_info *info = get_svr4_info (current_program_space);
>     return info->active_namespaces.size ();
>   }
>   
> -/* See solib_ops::get_solibs_in_ns in solist.h.  */
> -static std::vector<const solib *>
> -svr4_get_solibs_in_ns (int nsid)
> +std::vector<const solib *>
> +svr4_solib_ops::get_solibs_in_ns (int nsid) const
>   {
>     std::vector<const solib*> ns_solibs;
>     svr4_info *info = get_svr4_info (current_program_space);
> @@ -3791,26 +3744,6 @@ svr4_get_solibs_in_ns (int nsid)
>     return ns_solibs;
>   }
>   
> -const struct solib_ops svr4_so_ops =
> -{
> -  svr4_relocate_section_addresses,
> -  svr4_clear_so,
> -  svr4_clear_solib,
> -  svr4_solib_create_inferior_hook,
> -  svr4_current_sos,
> -  open_symbol_file_object,
> -  svr4_in_dynsym_resolve_code,
> -  solib_bfd_open,
> -  svr4_same,
> -  svr4_keep_data_in_core,
> -  svr4_update_solib_event_breakpoints,
> -  svr4_handle_solib_event,
> -  svr4_find_solib_addr,
> -  svr4_find_solib_ns,
> -  svr4_num_active_namespaces,
> -  svr4_get_solibs_in_ns,
> -};
> -
>   void _initialize_svr4_solib ();
>   void
>   _initialize_svr4_solib ()
> diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
> index 1ff9be78a43d..a7be4c902e75 100644
> --- a/gdb/solib-svr4.h
> +++ b/gdb/solib-svr4.h
> @@ -20,12 +20,15 @@
>   #ifndef GDB_SOLIB_SVR4_H
>   #define GDB_SOLIB_SVR4_H
>   
> +#include "gdbarch.h"
>   #include "solib.h"
>   
>   struct objfile;
> -struct solib_ops;
> -
> -extern const solib_ops svr4_so_ops;
> +struct link_map_offsets;
> +struct probe_and_action;
> +struct svr4_info;
> +struct svr4_library_list;
> +struct svr4_so;
>   
>   /* Link map info to include in an allocated solib entry.  */
>   
> @@ -50,6 +53,101 @@ struct lm_info_svr4 final : public lm_info
>   
>   using lm_info_svr4_up = std::unique_ptr<lm_info_svr4>;
>   
> +/* What to do when a probe stop occurs.  */
> +
> +enum probe_action
> +{
> +  /* Something went seriously wrong.  Stop using probes and
> +     revert to using the older interface.  */
> +  PROBES_INTERFACE_FAILED,
> +
> +  /* No action is required.  The shared object list is still
> +     valid.  */
> +  DO_NOTHING,
> +
> +  /* The shared object list should be reloaded entirely.  */
> +  FULL_RELOAD,
> +
> +  /* Attempt to incrementally update the shared object list. If
> +     the update fails or is not possible, fall back to reloading
> +     the list in full.  */
> +  UPDATE_OR_RELOAD,
> +};
> +
> +/* solib_ops for SVR4 systems.  */
> +
> +struct svr4_solib_ops : public solib_ops
> +{
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  void clear_so (const solib &so) const override;
> +  void clear_solib (program_space *pspace) override;
> +  void create_inferior_hook (int from_tty) override;
> +  owning_intrusive_list<solib> current_sos () override;
> +  bool open_symbol_file_object (int from_tty) override;
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> +  bool same (const solib &gdb, const solib &inferior) const override;
> +  bool keep_data_in_core (CORE_ADDR vaddr, unsigned long size) override;
> +  void update_breakpoints () const override;
> +  void handle_event () override;
> +  std::optional<CORE_ADDR> find_solib_addr (solib &so) const override;
> +  bool supports_namespaces () const override { return true; }
> +  int find_solib_ns (const solib &so) const override;
> +  int num_active_namespaces () const override;
> +  std::vector<const solib *> get_solibs_in_ns (int nsid) const override;
> +
> +  /* Return the appropriate link map offsets table for the architecture.  */
> +  virtual link_map_offsets *fetch_link_map_offsets () const = 0;
> +
> +  /* This needs to be public because it's accessed from an observer.  */
> +  void current_sos_direct (svr4_info *info) const;
> +
> +private:
> +  void create_probe_breakpoints (svr4_info *info, gdbarch *gdbarch,
> +				 const std::vector<probe *> *probes,
> +				 objfile *objfile) const;
> +  bool find_and_create_probe_breakpoints (svr4_info *info, gdbarch *gdbarch,
> +					  obj_section *os,
> +					  bool with_prefix) const;
> +  void create_event_breakpoints (svr4_info *info, gdbarch *gdbarch,
> +				 CORE_ADDR address) const;
> +  int enable_break (svr4_info *info, int from_tty) const;
> +  bool is_default_namespace (CORE_ADDR debug_base) const;
> +  void free_probes_table (svr4_info *info) const;
> +  CORE_ADDR find_r_brk (svr4_info *info) const;
> +  CORE_ADDR find_r_ldsomap (svr4_info *info) const;
> +  owning_intrusive_list<solib> default_sos (svr4_info *info);
> +  int read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
> +		    std::vector<svr4_so> &sos, int ignore_first) const;
> +  lm_info_svr4_up read_lm_info (CORE_ADDR lm_addr) const;
> +  int has_lm_dynamic_from_link_map () const;
> +  CORE_ADDR lm_addr_check (const solib &so, bfd *abfd) const;
> +  CORE_ADDR read_r_next (CORE_ADDR debug_base) const;
> +  CORE_ADDR read_r_map (CORE_ADDR debug_base) const;
> +  int parse_libraries (const char *document, svr4_library_list *list);
> +  int current_sos_via_xfer_libraries (svr4_library_list *list,
> +				      const char *annex) const;
> +  owning_intrusive_list<solib> collect_probes_sos (svr4_info *info);
> +  owning_intrusive_list<solib> current_sos_1 (svr4_info *info);
> +  owning_intrusive_list<solib> solibs_from_svr4_sos
> +    (const std::vector<svr4_so> &sos);
> +  void register_event_probe (objfile *objfile, probe *prob, CORE_ADDR address,
> +			     enum probe_action action) const;
> +  void disable_probes_interface (svr4_info *info) const;
> +  probe_and_action *event_probe_at (CORE_ADDR address) const;
> +  void update_full (svr4_info *info) const;
> +  int update_incremental (svr4_info *info, CORE_ADDR debug_base,
> +			  CORE_ADDR lm) const;
> +  bool update_event_breakpoint (breakpoint *b) const;
> +  CORE_ADDR find_debug_base (const solib *solib) const;
> +};
> +
> +/* solib_ops for ILP32 SVR4 systems.  */
> +
> +struct ilp32_svr4_solib_ops : public svr4_solib_ops
> +{
> +  link_map_offsets *fetch_link_map_offsets () const override;
> +};
> +
>   /* Critical offsets and sizes which describe struct r_debug and
>      struct link_map on SVR4-like targets.  All offsets and sizes are
>      in bytes unless otherwise specified.  */
> @@ -91,26 +189,22 @@ struct link_map_offsets
>       int l_name_offset;
>     };
>   
> -/* set_solib_svr4_fetch_link_map_offsets() is intended to be called by
> -   a <arch>_gdbarch_init() function.  It is used to establish an
> -   architecture specific link_map_offsets fetcher for the architecture
> -   being defined.  */
> +/* Set the gdbarch methods for SVR4 systems.  */
>   
> -extern void set_solib_svr4_fetch_link_map_offsets
> -  (struct gdbarch *gdbarch, struct link_map_offsets *(*func) (void));
> +extern void set_solib_svr4_ops (gdbarch *gdbarch,
> +				gdbarch_new_solib_ops_ftype new_solib_ops);
>   
>   /* This function is called by thread_db.c.  Return the address of the
>      link map for the given objfile.  */
>   extern CORE_ADDR svr4_fetch_objfile_link_map (struct objfile *objfile);
>   
> -/* Fetch (and possibly build) an appropriate `struct link_map_offsets'
> -   for ILP32 and LP64 SVR4 systems.  */
> -extern struct link_map_offsets *svr4_ilp32_fetch_link_map_offsets (void);
> -extern struct link_map_offsets *svr4_lp64_fetch_link_map_offsets (void);
> +/* Return a new solib_ops for ILP32 SVR4 systems.  */
>   
> -/* Return true if PC lies in the dynamic symbol resolution code of the
> -   SVR4 run time loader.  */
> -bool svr4_in_dynsym_resolve_code (CORE_ADDR pc);
> +extern solib_ops_up new_svr4_ilp32_solib_ops ();
> +
> +/* Return a new solib_ops for LP64 SVR4 systems.  */
> +
> +extern solib_ops_up new_svr4_lp64_solib_ops ();
>   
>   /* For the MUSL C library, given link map address LM_ADDR, return the
>      corresponding TLS module id, or 0 if not found.  */
> diff --git a/gdb/solib-target.c b/gdb/solib-target.c
> index 61b841928ff8..5b26f5be34a8 100644
> --- a/gdb/solib-target.c
> +++ b/gdb/solib-target.c
> @@ -209,6 +209,14 @@ static const struct gdb_xml_element library_list_elements[] = {
>     { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
>   };
>   
> +/* See solib-target.h.  */
> +
> +solib_ops_up
> +new_target_solib_ops ()
> +{
> +  return std::make_unique<target_solib_ops> ();
> +}
> +
>   static std::vector<lm_info_target_up>
>   solib_target_parse_libraries (const char *library)
>   {
> @@ -226,8 +234,8 @@ solib_target_parse_libraries (const char *library)
>   }
>   #endif
>   
> -static owning_intrusive_list<solib>
> -solib_target_current_sos (void)
> +owning_intrusive_list<solib>
> +target_solib_ops::current_sos ()
>   {
>     owning_intrusive_list<solib> sos;
>   
> @@ -245,7 +253,7 @@ solib_target_current_sos (void)
>     /* Build a struct solib for each entry on the list.  */
>     for (lm_info_target_up &info : library_list)
>       {
> -      auto &new_solib = sos.emplace_back (solib_target_so_ops);
> +      auto &new_solib = sos.emplace_back (*this);
>   
>         /* We don't need a copy of the name in INFO anymore.  */
>         new_solib.name = std::move (info->name);
> @@ -256,8 +264,9 @@ solib_target_current_sos (void)
>     return sos;
>   }
>   
> -static void
> -solib_target_relocate_section_addresses (solib &so, target_section *sec)
> +void
> +target_solib_ops::relocate_section_addresses (solib &so,
> +					      target_section *sec) const
>   {
>     CORE_ADDR offset;
>     auto *li = gdb::checked_static_cast<lm_info_target *> (so.lm_info.get ());
> @@ -376,28 +385,11 @@ Could not relocate shared library \"%s\": bad offsets"), so.name.c_str ());
>     sec->endaddr += offset;
>   }
>   
> -static bool
> -solib_target_in_dynsym_resolve_code (CORE_ADDR pc)
> +bool
> +target_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
>   {
>     /* We don't have a range of addresses for the dynamic linker; there
>        may not be one in the program's address space.  So only report
>        PLT entries (which may be import stubs).  */
>     return in_plt_section (pc);
>   }
> -
> -const solib_ops solib_target_so_ops =
> -{
> -  solib_target_relocate_section_addresses,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  solib_target_current_sos,
> -  nullptr,
> -  solib_target_in_dynsym_resolve_code,
> -  solib_bfd_open,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  nullptr,
> -  default_find_solib_addr,
> -};
> diff --git a/gdb/solib-target.h b/gdb/solib-target.h
> index f8a22fd6f6ad..8a5f8432345b 100644
> --- a/gdb/solib-target.h
> +++ b/gdb/solib-target.h
> @@ -20,7 +20,19 @@
>   #ifndef GDB_SOLIB_TARGET_H
>   #define GDB_SOLIB_TARGET_H
>   
> -struct solib_ops;
> -extern const solib_ops solib_target_so_ops;
> +#include "solib.h"
> +
> +/* solib_ops for systems fetching solibs from the target.  */
> +
> +struct target_solib_ops : solib_ops
> +{
> +  void relocate_section_addresses (solib &so, target_section *) const override;
> +  owning_intrusive_list<solib> current_sos () override;
> +  bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> +};
> +
> +/* Return a new solib_ops for systems fetching solibs from the target.  */
> +
> +solib_ops_up new_target_solib_ops ();
>   
>   #endif /* GDB_SOLIB_TARGET_H */
> diff --git a/gdb/solib.c b/gdb/solib.c
> index dceab2b865bf..c0e5f803882a 100644
> --- a/gdb/solib.c
> +++ b/gdb/solib.c
> @@ -467,6 +467,12 @@ solib_bfd_open (const char *pathname)
>     return abfd;
>   }
>   
> +gdb_bfd_ref_ptr
> +solib_ops::bfd_open (const char *pathname)
> +{
> +  return solib_bfd_open (pathname);
> +}
> +
>   /* Given a pointer to one of the shared objects in our list of mapped
>      objects, use the recorded name to open a bfd descriptor for the
>      object, build a section table, relocate all the section addresses
> @@ -598,8 +604,7 @@ solib::clear ()
>     this->name = this->original_name;
>   
>     /* Do the same for target-specific data.  */
> -  if (this->ops ().clear_so != NULL)
> -    this->ops ().clear_so (*this);
> +  this->ops ().clear_so (*this);
>   }
>   
>   lm_info::~lm_info () = default;
> @@ -707,7 +712,7 @@ notify_solib_unloaded (program_space *pspace, const solib &so,
>   void
>   update_solib_list (int from_tty)
>   {
> -  const solib_ops *ops = current_program_space->solib_ops ();
> +  solib_ops *ops = current_program_space->solib_ops ();
>   
>     if (ops == nullptr)
>       return;
> @@ -722,8 +727,7 @@ update_solib_list (int from_tty)
>   	 have not opened a symbol file, we may be able to get its
>   	 symbols now!  */
>         if (inf->attach_flag
> -	  && current_program_space->symfile_object_file == nullptr
> -	  && ops->open_symbol_file_object != nullptr)
> +	  && current_program_space->symfile_object_file == nullptr)
>   	{
>   	  try
>   	    {
> @@ -772,19 +776,8 @@ update_solib_list (int from_tty)
>         /* Check to see whether the shared object *gdb also appears in
>   	 the inferior's current list.  */
>         for (; inferior_iter != inferior.end (); ++inferior_iter)
> -	{
> -	  if (ops->same)
> -	    {
> -	      if (ops->same (*gdb_iter, *inferior_iter))
> -		break;
> -	    }
> -	  else
> -	    {
> -	      if (!filename_cmp (gdb_iter->original_name.c_str (),
> -				 inferior_iter->original_name.c_str ()))
> -		break;
> -	    }
> -	}
> +	if (ops->same (*gdb_iter, *inferior_iter))
> +	  break;
>   
>         /* If the shared object appears on the inferior's list too, then
>   	 it's still loaded, so we don't need to do anything.  Delete
> @@ -1032,8 +1025,8 @@ print_solib_list_table (std::vector<const solib *> solib_list,
>        first PRINT_NAMESPACE has to be true, second the solib_ops has to
>        support multiple namespaces, and third there must be more than one
>        active namespace.  Fold all these into the PRINT_NAMESPACE condition.  */
> -  print_namespace = print_namespace && ops->num_active_namespaces != nullptr
> -		    && ops->num_active_namespaces () > 1;
> +  print_namespace = (print_namespace && ops->supports_namespaces ()
> +		     && ops->num_active_namespaces () > 1);
>   
>     int num_cols = 4;
>     if (print_namespace)
> @@ -1162,7 +1155,7 @@ info_linker_namespace_command (const char *pattern, int from_tty)
>   
>     /* This command only really makes sense for inferiors that support
>        linker namespaces, so we can leave early.  */
> -  if (ops->num_active_namespaces == nullptr)
> +  if (!ops->supports_namespaces ())
>       error (_("Current inferior does not support linker namespaces." \
>   	     "Use \"info sharedlibrary\" instead"));
>   
> @@ -1272,17 +1265,21 @@ solib_name_from_address (struct program_space *pspace, CORE_ADDR address)
>     return nullptr;
>   }
>   
> +bool
> +solib_ops::same (const solib &a, const solib &b) const
> +{
> +  return (filename_cmp (a.original_name.c_str (), b.original_name.c_str ())
> +	  == 0);
> +}
> +
>   /* See solib.h.  */
>   
>   bool
>   solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
>   {
> -  const solib_ops *ops = current_program_space->solib_ops ();
> +  solib_ops *ops = current_program_space->solib_ops ();
>   
> -  if (ops != nullptr && ops->keep_data_in_core != nullptr)
> -    return ops->keep_data_in_core (vaddr, size) != 0;
> -  else
> -    return false;
> +  return ops != nullptr && ops->keep_data_in_core (vaddr, size);
>   }
>   
>   /* See solib.h.  */
> @@ -1301,9 +1298,8 @@ clear_solib (program_space *pspace)
>   
>     pspace->solibs ().clear ();
>   
> -  const solib_ops *ops = pspace->solib_ops ();
> -
> -  if (ops != nullptr && ops->clear_solib != nullptr)
> +  if (solib_ops *ops = pspace->solib_ops ();
> +      ops != nullptr)
>       ops->clear_solib (pspace);
>   }
>   
> @@ -1315,10 +1311,9 @@ clear_solib (program_space *pspace)
>   void
>   solib_create_inferior_hook (int from_tty)
>   {
> -  const solib_ops *ops = current_program_space->solib_ops ();
> -
> -  if (ops != nullptr && ops->solib_create_inferior_hook != nullptr)
> -    ops->solib_create_inferior_hook (from_tty);
> +  if (solib_ops *ops = current_program_space->solib_ops ();
> +      ops != nullptr)
> +    ops->create_inferior_hook (from_tty);
>   }
>   
>   /* See solib.h.  */
> @@ -1328,8 +1323,7 @@ in_solib_dynsym_resolve_code (CORE_ADDR pc)
>   {
>     const solib_ops *ops = current_program_space->solib_ops ();
>   
> -  return (ops != nullptr && ops->in_dynsym_resolve_code != nullptr
> -	  && ops->in_dynsym_resolve_code (pc));
> +  return ops != nullptr && ops->in_dynsym_resolve_code (pc);
>   }
>   
>   /* Implements the "sharedlibrary" command.  */
> @@ -1373,7 +1367,7 @@ update_solib_breakpoints (void)
>   {
>     const solib_ops *ops = current_program_space->solib_ops ();
>   
> -  if (ops != nullptr && ops->update_breakpoints != nullptr)
> +  if (ops != nullptr)
>       ops->update_breakpoints ();
>   }
>   
> @@ -1382,9 +1376,8 @@ update_solib_breakpoints (void)
>   void
>   handle_solib_event (void)
>   {
> -  const solib_ops *ops = current_program_space->solib_ops ();
> -
> -  if (ops != nullptr && ops->handle_event != nullptr)
> +  if (solib_ops *ops = current_program_space->solib_ops ();
> +      ops != nullptr)
>       ops->handle_event ();
>   
>     current_inferior ()->pspace->clear_solib_cache ();
> @@ -1417,7 +1410,8 @@ reload_shared_libraries_1 (int from_tty)
>   
>         gdb::unique_xmalloc_ptr<char> filename (
>   	tilde_expand (so.original_name.c_str ()));
> -      gdb_bfd_ref_ptr abfd (solib_bfd_open (filename.get ()));
> +
> +      gdb_bfd_ref_ptr abfd = so.ops ().bfd_open (filename.get ());
>         if (abfd != NULL)
>   	found_pathname = bfd_get_filename (abfd.get ());
>   
> @@ -1479,11 +1473,10 @@ reload_shared_libraries (const char *ignored, int from_tty,
>        about ld.so.  */
>     if (target_has_execution ())
>       {
> -      const solib_ops *ops = current_program_space->solib_ops ();
> -
>         /* Reset or free private data structures not associated with
>   	 solib entries.  */
> -      if (ops != nullptr && ops->clear_solib != nullptr)
> +      if (solib_ops *ops = current_program_space->solib_ops ();
> +	  ops != nullptr)
>   	ops->clear_solib (current_program_space);
>   
>         /* Remove any previous solib event breakpoint.  This is usually
> @@ -1810,15 +1803,8 @@ remove_user_added_objfile (struct objfile *objfile)
>       }
>   }
>   
> -/* See solist.h.  */
> -
> -std::optional<CORE_ADDR>
> -default_find_solib_addr (solib &so)
> -{
> -  return {};
> -}
> -
>   /* Implementation of the linker_namespace convenience variable.
> +
>      This returns the GDB internal identifier of the linker namespace,
>      for the selected frame, as an integer.  If the inferior doesn't support
>      linker namespaces, this always returns 0.  */
> @@ -1833,7 +1819,7 @@ linker_namespace_make_value (gdbarch *gdbarch, internalvar *var,
>     for (const solib &so : current_program_space->solibs ())
>       if (solib_contains_address_p (so, curr_pc))
>         {
> -	if (so.ops ().find_solib_ns != nullptr)
> +	if (so.ops ().supports_namespaces ())
>   	  nsid = so.ops ().find_solib_ns (so);
>   
>   	break;
> diff --git a/gdb/solib.h b/gdb/solib.h
> index 09d56c08b953..a3104e47e40e 100644
> --- a/gdb/solib.h
> +++ b/gdb/solib.h
> @@ -61,10 +61,10 @@ struct solib : intrusive_list_node<solib>
>     /* Constructor
>   
>        OPS is the solib_ops implementation providing this solib.  */
> -  explicit solib (const solib_ops &ops) : m_ops (&ops) {}
> +  explicit solib (solib_ops &ops) : m_ops (&ops) {}
>   
>     /* Return the solib_ops implementation providing this solib.  */
> -  const solib_ops &ops () const
> +  solib_ops &ops () const
>     { return *m_ops; }
>   
>     /* Free symbol-file related contents of SO and reset for possible reloading
> @@ -125,7 +125,7 @@ struct solib : intrusive_list_node<solib>
>   
>   private:
>     /* The solib_ops responsible for this solib.  */
> -  const solib_ops *m_ops;
> +  solib_ops *m_ops;
>   };
>   
>   /* A unique pointer to an solib.  */
> @@ -133,22 +133,31 @@ using solib_up = std::unique_ptr<solib>;
>   
>   struct solib_ops
>   {
> +  virtual ~solib_ops () = default;
> +
>     /* Adjust the section binding addresses by the base address at
>        which the object was actually mapped.  */
> -  void (*relocate_section_addresses) (solib &so, target_section *);
> +  virtual void relocate_section_addresses (solib &so, target_section *) const
> +    = 0;
>   
>     /* Reset private data structures associated with SO.
>        This is called when SO is about to be reloaded.
> -     It is also called when SO is about to be freed.  */
> -  void (*clear_so) (const solib &so);
> +     It is also called when SO is about to be freed.
> +
> +     Defaults to no-op.  */
> +  virtual void clear_so (const solib &so) const {}
>   
>     /* Free private data structures associated to PSPACE.  This method
>        should not free resources associated to individual solib entries,
> -     those are cleared by the clear_so method.  */
> -  void (*clear_solib) (program_space *pspace);
> +     those are cleared by the clear_so method.
>   
> -  /* Target dependent code to run after child process fork.  */
> -  void (*solib_create_inferior_hook) (int from_tty);
> +     Defaults to no-op.  */
> +  virtual void clear_solib (program_space *pspace) {}
> +
> +  /* Target dependent code to run after child process fork.
> +
> +     Defaults to no-op.  */
> +  virtual void create_inferior_hook (int from_tty) {};
>   
>     /* Construct a list of the currently loaded shared objects.  This
>        list does not include an entry for the main executable file.
> @@ -157,45 +166,51 @@ struct solib_ops
>        inferior --- we don't examine any of the shared library files
>        themselves.  The declaration of `struct solib' says which fields
>        we provide values for.  */
> -  owning_intrusive_list<solib> (*current_sos) ();
> +  virtual owning_intrusive_list<solib> current_sos () = 0;
>   
>     /* Find, open, and read the symbols for the main executable.  If
> -     FROM_TTY is non-zero, allow messages to be printed.  */
> -  int (*open_symbol_file_object) (int from_ttyp);
> +     FROM_TTY is non-zero, allow messages to be printed.
> +
> +     Return true if this was done successfully.  Defaults to false.  */
> +  virtual bool open_symbol_file_object (int from_tty) { return false; }
>   
>     /* Determine if PC lies in the dynamic symbol resolution code of
> -     the run time loader.  */
> -  bool (*in_dynsym_resolve_code) (CORE_ADDR pc);
> +     the run time loader.
> +
> +     Defaults to false.  */
> +  virtual bool in_dynsym_resolve_code (CORE_ADDR pc) const
> +  { return false; };
>   
>     /* Find and open shared library binary file.  */
> -  gdb_bfd_ref_ptr (*bfd_open) (const char *pathname);
> +  virtual gdb_bfd_ref_ptr bfd_open (const char *pathname);
>   
> -  /* Given two solib objects, one from the GDB thread list
> -     and another from the list returned by current_sos, return 1
> -     if they represent the same library.
> -     Falls back to using strcmp on ORIGINAL_NAME when set to nullptr.  */
> -  int (*same) (const solib &gdb, const solib &inferior);
> +  /* Given two solib objects, GDB from the GDB thread list and INFERIOR from the
> +     list returned by current_sos, return true if they represent the same library.
> +
> +     Defaults to comparing the solib original names using filename_cmp.  */
> +  virtual bool same (const solib &gdb, const solib &inferior) const;
>   
>     /* Return whether a region of memory must be kept in a core file
>        for shared libraries loaded before "gcore" is used to be
>        handled correctly when the core file is loaded.  This only
>        applies when the section would otherwise not be kept in the
> -     core file (in particular, for readonly sections).  */
> -  int (*keep_data_in_core) (CORE_ADDR vaddr,
> -			    unsigned long size);
> +     core file (in particular, for readonly sections).
>   
> -  /* Enable or disable optional solib event breakpoints as
> -     appropriate.  This should be called whenever
> -     stop_on_solib_events is changed.  This pointer can be
> -     NULL, in which case no enabling or disabling is necessary
> -     for this target.  */
> -  void (*update_breakpoints) (void);
> +     Defaults to false.  */
> +  virtual bool keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
> +  { return false; };
>   
> -  /* Target-specific processing of solib events that will be
> -     performed before solib_add is called.  This pointer can be
> -     NULL, in which case no specific preprocessing is necessary
> -     for this target.  */
> -  void (*handle_event) (void);
> +  /* Enable or disable optional solib event breakpoints as appropriate.  This
> +     should be called whenever stop_on_solib_events is changed.
> +
> +     Defaults to no-op.  */
> +  virtual void update_breakpoints () const {};
> +
> +  /* Target-specific processing of solib events that will be performed before
> +     solib_add is called.
> +
> +     Defaults to no-op.  */
> +  virtual void handle_event () {};
>   
>     /* Return an address within the inferior's address space which is known
>        to be part of SO.  If there is no such address, or GDB doesn't know
> @@ -210,28 +225,45 @@ struct solib_ops
>        mapped file, and thus to a build-id.  GDB can then use this
>        information to help locate the shared library objfile, if the objfile
>        is not in the expected place (as defined by the shared libraries file
> -     name).  */
> -  std::optional<CORE_ADDR> (*find_solib_addr) (solib &so);
> +     name).
>   
> -  /* Return which linker namespace contains the current so.
> -     If the linker or libc does not support linkage namespaces at all
> -     (which is basically all of them but solib-svr4), this function should
> -     be set to nullptr, so that "info shared" won't add an unnecessary
> -     column.
> +     The default implementation of returns an empty option, indicating GDB is
> +     unable to find an address within the library SO.  */
> +  virtual std::optional<CORE_ADDR> find_solib_addr (solib &so) const
> +  { return {}; };
>   
> -     If the namespace can not be determined (such as when we're stepping
> -     though the dynamic linker), this function should throw a
> -     gdb_exception_error.  */
> -  int (*find_solib_ns) (const solib &so);
> +  /* Return true if the linker or libc supports linkage namespaces.
>   
> -  /* Returns the number of active namespaces in the inferior.  */
> -  int (*num_active_namespaces) ();
> +     Defaults to false.  */
> +  virtual bool supports_namespaces () const { return false; }
> +
> +  /* Return which linker namespace contains SO.
> +
> +     The supports_namespaces method must return true for this to be
> +     called.
> +
> +     Throw an error if the namespace can not be determined (such as when we're
> +     stepping though the dynamic linker).  */
> +  virtual int find_solib_ns (const solib &so) const
> +  { gdb_assert_not_reached ("namespaces not supported"); }
> +
> +  /* Returns the number of active namespaces in the inferior.
> +
> +     The supports_namespaces method must return true for this to be called.  */
> +  virtual int num_active_namespaces () const
> +  { gdb_assert_not_reached ("namespaces not supported"); }
>   
>     /* Returns all solibs for a given namespace.  If the namespace is not
> -     active, returns an empty vector.  */
> -  std::vector<const solib *> (*get_solibs_in_ns) (int ns);
> +     active, returns an empty vector.
> +
> +     The supports_namespaces method must return true for this to be called.  */
> +  virtual std::vector<const solib *> get_solibs_in_ns (int ns) const
> +  { gdb_assert_not_reached ("namespaces not supported"); }
>   };
>   
> +/* A unique pointer to an solib_ops.  */
> +using solib_ops_up = std::unique_ptr<solib_ops>;
> +
>   /* Find main executable binary file.  */
>   extern gdb::unique_xmalloc_ptr<char> exec_file_find (const char *in_pathname,
>   						     int *fd);
> @@ -246,11 +278,6 @@ extern gdb_bfd_ref_ptr solib_bfd_fopen (const char *pathname, int fd);
>   /* Find solib binary file and open it.  */
>   extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname);
>   
> -/* A default implementation of the solib_ops::find_solib_addr callback.
> -   This just returns an empty std::optional<CORE_ADDR> indicating GDB is
> -   unable to find an address within the library SO.  */
> -extern std::optional<CORE_ADDR> default_find_solib_addr (solib &so);
> -
>   /* Called when we free all symtabs of PSPACE, to free the shared library
>      information as well.  */
>   
> diff --git a/gdb/sparc-linux-tdep.c b/gdb/sparc-linux-tdep.c
> index 27706b72c7ef..06f6e06ecc9b 100644
> --- a/gdb/sparc-linux-tdep.c
> +++ b/gdb/sparc-linux-tdep.c
> @@ -33,6 +33,7 @@
>   #include "tramp-frame.h"
>   #include "xml-syscall.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   
>   /* The syscall's XML filename for sparc 32-bit.  */
>   #define XML_SYSCALL_FILENAME_SPARC32 "syscalls/sparc-linux.xml"
> @@ -436,8 +437,7 @@ sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux has SVR4-style shared libraries...  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     /* ...which means that we need some special handling when doing
>        prologue analysis.  */
> diff --git a/gdb/sparc-netbsd-tdep.c b/gdb/sparc-netbsd-tdep.c
> index 84e3a979c6cd..1e9305440d76 100644
> --- a/gdb/sparc-netbsd-tdep.c
> +++ b/gdb/sparc-netbsd-tdep.c
> @@ -313,8 +313,7 @@ sparc32nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     frame_unwind_append_unwinder (gdbarch, &sparc32nbsd_sigcontext_frame_unwind);
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   
>   void _initialize_sparcnbsd_tdep ();
> diff --git a/gdb/sparc-sol2-tdep.c b/gdb/sparc-sol2-tdep.c
> index 5c6085acabbf..51d93910630c 100644
> --- a/gdb/sparc-sol2-tdep.c
> +++ b/gdb/sparc-sol2-tdep.c
> @@ -207,8 +207,7 @@ sparc32_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* Solaris has SVR4-style shared libraries...  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     /* ...which means that we need some special handling when doing
>        prologue analysis.  */
> diff --git a/gdb/sparc64-fbsd-tdep.c b/gdb/sparc64-fbsd-tdep.c
> index 738c3d11c59c..9adbcce01b77 100644
> --- a/gdb/sparc64-fbsd-tdep.c
> +++ b/gdb/sparc64-fbsd-tdep.c
> @@ -238,8 +238,7 @@ sparc64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* FreeBSD/sparc64 has SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   }
>   
>   void _initialize_sparc64fbsd_tdep ();
> diff --git a/gdb/sparc64-linux-tdep.c b/gdb/sparc64-linux-tdep.c
> index 6e7281c91882..af15175b0795 100644
> --- a/gdb/sparc64-linux-tdep.c
> +++ b/gdb/sparc64-linux-tdep.c
> @@ -32,6 +32,7 @@
>   #include "tramp-frame.h"
>   #include "xml-syscall.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   
>   /* ADI specific si_code */
>   #ifndef SEGV_ACCADI
> @@ -383,8 +384,7 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux has SVR4-style shared libraries...  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>     /* ...which means that we need some special handling when doing
>        prologue analysis.  */
> diff --git a/gdb/sparc64-netbsd-tdep.c b/gdb/sparc64-netbsd-tdep.c
> index 19195987451c..8b3e013be562 100644
> --- a/gdb/sparc64-netbsd-tdep.c
> +++ b/gdb/sparc64-netbsd-tdep.c
> @@ -266,8 +266,7 @@ sparc64nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* NetBSD/sparc64 has SVR4-style shared libraries.  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   }
>   
>   void _initialize_sparc64nbsd_tdep ();
> diff --git a/gdb/sparc64-obsd-tdep.c b/gdb/sparc64-obsd-tdep.c
> index 657f548537e7..cd8f3288995b 100644
> --- a/gdb/sparc64-obsd-tdep.c
> +++ b/gdb/sparc64-obsd-tdep.c
> @@ -440,8 +440,7 @@ sparc64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     obsd_init_abi (info, gdbarch);
>   
>     /* OpenBSD/sparc64 has SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>     set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver);
>   
>     /* OpenBSD provides a user-level threads implementation.  */
> diff --git a/gdb/sparc64-sol2-tdep.c b/gdb/sparc64-sol2-tdep.c
> index 72a07ecef8ce..bb906efd29b4 100644
> --- a/gdb/sparc64-sol2-tdep.c
> +++ b/gdb/sparc64-sol2-tdep.c
> @@ -214,8 +214,7 @@ sparc64_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* Solaris has SVR4-style shared libraries...  */
>     set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_lp64_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_lp64_solib_ops);
>   
>     /* ...which means that we need some special handling when doing
>        prologue analysis.  */
> diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
> index 280d46dc487b..4dfb6f280bb7 100644
> --- a/gdb/tic6x-linux-tdep.c
> +++ b/gdb/tic6x-linux-tdep.c
> @@ -169,7 +169,7 @@ tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     linux_init_abi (info, gdbarch, 0);
>   
>     /* Shared library handling.  */
> -  set_gdbarch_so_ops (gdbarch, &dsbt_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_dsbt_solib_ops);
>   
>     tdep->syscall_next_pc = tic6x_linux_syscall_next_pc;
>   
> diff --git a/gdb/tilegx-linux-tdep.c b/gdb/tilegx-linux-tdep.c
> index a0e69543f88b..92e5d0418b12 100644
> --- a/gdb/tilegx-linux-tdep.c
> +++ b/gdb/tilegx-linux-tdep.c
> @@ -19,6 +19,7 @@
>   
>   #include "osabi.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "glibc-tdep.h"
>   #include "solib-svr4.h"
>   #include "symtab.h"
> @@ -119,11 +120,9 @@ tilegx_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     /* GNU/Linux uses SVR4-style shared libraries.  */
>     if (arch_size == 32)
> -    set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					   linux_ilp32_fetch_link_map_offsets);
> +    set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>     else
> -    set_solib_svr4_fetch_link_map_offsets (gdbarch,
> -					   linux_lp64_fetch_link_map_offsets);
> +    set_solib_svr4_ops (gdbarch, new_linux_lp64_svr4_solib_ops);
>   
>     /* Enable TLS support.  */
>     set_gdbarch_fetch_tls_load_module_address (gdbarch,
> diff --git a/gdb/vax-netbsd-tdep.c b/gdb/vax-netbsd-tdep.c
> index 34a9150fdb27..fabd98218d4b 100644
> --- a/gdb/vax-netbsd-tdep.c
> +++ b/gdb/vax-netbsd-tdep.c
> @@ -32,8 +32,7 @@ vaxnbsd_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>     nbsd_init_abi (info, gdbarch);
>   
>     /* NetBSD ELF uses SVR4-style shared libraries.  */
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   }
>   
>   void _initialize_vaxnbsd_tdep ();
> diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
> index d8858a7f1450..75437a145d6a 100644
> --- a/gdb/windows-tdep.c
> +++ b/gdb/windows-tdep.c
> @@ -862,10 +862,25 @@ windows_get_siginfo_type (struct gdbarch *gdbarch)
>     return siginfo_type;
>   }
>   
> +/* solib_ops for Windows systems.  */
> +
> +struct windows_solib_ops : target_solib_ops
> +{
> +  void create_inferior_hook (int from_tty) override;
> +};
> +
> +/* Return a new solib_ops for Windows systems.  */
> +
> +static solib_ops_up
> +new_windows_solib_ops ()
> +{
> +  return std::make_unique<windows_solib_ops> ();
> +}
> +
>   /* Implement the "solib_create_inferior_hook" solib_ops method.  */
>   
> -static void
> -windows_solib_create_inferior_hook (int from_tty)
> +void
> +windows_solib_ops::create_inferior_hook (int from_tty)
>   {
>     CORE_ADDR exec_base = 0;
>   
> @@ -910,8 +925,6 @@ windows_solib_create_inferior_hook (int from_tty)
>       }
>   }
>   
> -static solib_ops windows_so_ops;
> -
>   /* Common parts for gdbarch initialization for the Windows and Cygwin OS
>      ABIs.  */
>   
> @@ -928,10 +941,7 @@ windows_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch)
>     set_gdbarch_iterate_over_objfiles_in_search_order
>       (gdbarch, windows_iterate_over_objfiles_in_search_order);
>   
> -  windows_so_ops = solib_target_so_ops;
> -  windows_so_ops.solib_create_inferior_hook
> -    = windows_solib_create_inferior_hook;
> -  set_gdbarch_so_ops (gdbarch, &windows_so_ops);
> +  set_gdbarch_new_solib_ops (gdbarch, new_windows_solib_ops);
>   
>     set_gdbarch_get_siginfo_type (gdbarch, windows_get_siginfo_type);
>   }
> diff --git a/gdb/xtensa-linux-tdep.c b/gdb/xtensa-linux-tdep.c
> index b72d683b3b40..eaa46cf788ab 100644
> --- a/gdb/xtensa-linux-tdep.c
> +++ b/gdb/xtensa-linux-tdep.c
> @@ -20,6 +20,7 @@
>   #include "xtensa-tdep.h"
>   #include "osabi.h"
>   #include "linux-tdep.h"
> +#include "solib-svr4-linux.h"
>   #include "solib-svr4.h"
>   #include "symtab.h"
>   #include "gdbarch.h"
> @@ -111,8 +112,7 @@ xtensa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>   
>     linux_init_abi (info, gdbarch, 0);
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, linux_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_linux_ilp32_svr4_solib_ops);
>   
>     set_gdbarch_gdb_signal_from_target (gdbarch,
>   				      xtensa_linux_gdb_signal_from_target);
> diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c
> index a4bbffb8f114..85a6bee5b6f5 100644
> --- a/gdb/xtensa-tdep.c
> +++ b/gdb/xtensa-tdep.c
> @@ -3238,8 +3238,7 @@ xtensa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>     set_gdbarch_iterate_over_regset_sections
>       (gdbarch, xtensa_iterate_over_regset_sections);
>   
> -  set_solib_svr4_fetch_link_map_offsets
> -    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +  set_solib_svr4_ops (gdbarch, new_svr4_ilp32_solib_ops);
>   
>     /* Hook in the ABI-specific overrides, if they have been registered.  */
>     gdbarch_init_osabi (info, gdbarch);


  reply	other threads:[~2025-06-12 12:57 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-09 19:40 [PATCH v2 1/4] gdb/solib: add solib -> solib_ops backlink Simon Marchi
2025-06-09 19:40 ` [PATCH v2 2/4] gdb/solib: use solib::ops for operations that concern a single solib Simon Marchi
2025-06-09 19:40 ` [PATCH v2 3/4] gdb/progspace: add solib_ops pointer in program_space Simon Marchi
2025-06-11 18:14   ` Guinevere Larsen
2025-06-11 18:43     ` Simon Marchi
2025-06-16 18:41       ` Simon Marchi
2025-06-16 18:53         ` Simon Marchi
2025-06-16 19:38         ` Guinevere Larsen
2025-06-16 19:41           ` Simon Marchi
2025-06-09 19:40 ` [PATCH v2 4/4] gdb/solib: C++ify solib_ops Simon Marchi
2025-06-12 12:56   ` Guinevere Larsen [this message]
2025-06-12 15:02     ` Simon Marchi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fa840ac6-b0c1-41f4-ba87-9d0b5b326aac@redhat.com \
    --to=guinevere@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=simon.marchi@efficios.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox