Files
binutils-gdb/gdb/arm-netbsd-tdep.c
Simon Marchi a2e3cce344 gdb/solib: C++ify solib_ops
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_make_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_make_solib_ops`
callbacks.  For instance:

    solib_ops_up
    make_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 an instance of CRTP 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.

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 for 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
would 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
Approved-By: Pedro Alves <pedro@palves.net>
2025-06-26 14:08:31 -04:00

167 lines
5.4 KiB
C

/* Target-dependent code for NetBSD/arm.
Copyright (C) 2002-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 "osabi.h"
#include "arch/arm.h"
#include "arm-netbsd-tdep.h"
#include "netbsd-tdep.h"
#include "arm-tdep.h"
#include "regset.h"
#include "solib-svr4.h"
/* Description of the longjmp buffer. */
#define ARM_NBSD_JB_PC 24
#define ARM_NBSD_JB_ELEMENT_SIZE ARM_INT_REGISTER_SIZE
/* For compatibility with previous implementations of GDB on arm/NetBSD,
override the default little-endian breakpoint. */
static const gdb_byte arm_nbsd_arm_le_breakpoint[] = {0x11, 0x00, 0x00, 0xe6};
static const gdb_byte arm_nbsd_arm_be_breakpoint[] = {0xe6, 0x00, 0x00, 0x11};
static const gdb_byte arm_nbsd_thumb_le_breakpoint[] = {0xfe, 0xde};
static const gdb_byte arm_nbsd_thumb_be_breakpoint[] = {0xde, 0xfe};
/* This matches struct reg from NetBSD's sys/arch/arm/include/reg.h:
https://github.com/NetBSD/src/blob/7c13e6e6773bb171f4ed3ed53013e9d24b3c1eac/sys/arch/arm/include/reg.h#L39
*/
struct arm_nbsd_reg
{
uint32_t reg[13];
uint32_t sp;
uint32_t lr;
uint32_t pc;
uint32_t cpsr;
};
void
arm_nbsd_supply_gregset (const struct regset *regset, struct regcache *regcache,
int regnum, const void *gregs, size_t len)
{
const arm_nbsd_reg *gregset = static_cast<const arm_nbsd_reg *>(gregs);
gdb_assert (len >= sizeof (arm_nbsd_reg));
/* Integer registers. */
for (int i = ARM_A1_REGNUM; i < ARM_SP_REGNUM; i++)
if (regnum == -1 || regnum == i)
regcache->raw_supply (i, (char *) &gregset->reg[i]);
if (regnum == -1 || regnum == ARM_SP_REGNUM)
regcache->raw_supply (ARM_SP_REGNUM, (char *) &gregset->sp);
if (regnum == -1 || regnum == ARM_LR_REGNUM)
regcache->raw_supply (ARM_LR_REGNUM, (char *) &gregset->lr);
if (regnum == -1 || regnum == ARM_PC_REGNUM)
{
CORE_ADDR r_pc = gdbarch_addr_bits_remove (regcache->arch (), gregset->pc);
regcache->raw_supply (ARM_PC_REGNUM, (char *) &r_pc);
}
if (regnum == -1 || regnum == ARM_PS_REGNUM)
{
if (arm_apcs_32)
regcache->raw_supply (ARM_PS_REGNUM, (char *) &gregset->cpsr);
else
regcache->raw_supply (ARM_PS_REGNUM, (char *) &gregset->pc);
}
}
static const struct regset arm_nbsd_regset = {
nullptr,
arm_nbsd_supply_gregset,
/* We don't need a collect function because we only use this reading registers
(via iterate_over_regset_sections and fetch_regs/fetch_register). */
nullptr,
0
};
static void
arm_nbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
iterate_over_regset_sections_cb *cb,
void *cb_data,
const struct regcache *regcache)
{
cb (".reg", sizeof (arm_nbsd_reg), sizeof (arm_nbsd_reg), &arm_nbsd_regset,
NULL, cb_data);
/* cbiesinger/2020-02-12 -- as far as I can tell, ARM/NetBSD does
not write any floating point registers into the core file (tested
with NetBSD 9.1_RC1). When it does, this function will need to read them,
and the arm-netbsd gdbarch will need a core_read_description function
to return the right description for them. */
}
static void
arm_netbsd_init_abi_common (struct gdbarch_info info,
struct gdbarch *gdbarch)
{
arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
tdep->lowest_pc = 0x8000;
switch (info.byte_order)
{
case BFD_ENDIAN_LITTLE:
tdep->arm_breakpoint = arm_nbsd_arm_le_breakpoint;
tdep->thumb_breakpoint = arm_nbsd_thumb_le_breakpoint;
tdep->arm_breakpoint_size = sizeof (arm_nbsd_arm_le_breakpoint);
tdep->thumb_breakpoint_size = sizeof (arm_nbsd_thumb_le_breakpoint);
break;
case BFD_ENDIAN_BIG:
tdep->arm_breakpoint = arm_nbsd_arm_be_breakpoint;
tdep->thumb_breakpoint = arm_nbsd_thumb_be_breakpoint;
tdep->arm_breakpoint_size = sizeof (arm_nbsd_arm_be_breakpoint);
tdep->thumb_breakpoint_size = sizeof (arm_nbsd_thumb_be_breakpoint);
break;
default:
internal_error (_("arm_gdbarch_init: bad byte order for float format"));
}
tdep->jb_pc = ARM_NBSD_JB_PC;
tdep->jb_elt_size = ARM_NBSD_JB_ELEMENT_SIZE;
set_gdbarch_iterate_over_regset_sections
(gdbarch, arm_nbsd_iterate_over_regset_sections);
/* Single stepping. */
set_gdbarch_software_single_step (gdbarch, arm_software_single_step);
}
static void
arm_netbsd_elf_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch)
{
arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
arm_netbsd_init_abi_common (info, gdbarch);
nbsd_init_abi (info, gdbarch);
if (tdep->fp_model == ARM_FLOAT_AUTO)
tdep->fp_model = ARM_FLOAT_SOFT_VFP;
/* NetBSD ELF uses SVR4-style shared libraries. */
set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops);
}
INIT_GDB_FILE (arm_netbsd_tdep)
{
gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_NETBSD,
arm_netbsd_elf_init_abi);
}