* [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space
@ 2026-06-08 20:00 Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops Simon Marchi
` (9 more replies)
0 siblings, 10 replies; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
Looks like I'm having trouble with my outbound emails, I tried to fix
it, let me attempt a resend.
This is v2 of:
https://inbox.sourceware.org/20251209193610.296085-1-simon.marchi%40efficios.com
The changes mainly happened in the ROCm preparatory patches, following
review on v1. There were also some non-trivial rebase conflicts.
I have pushed patch "gdb/solib: use early return in solib_read_symbols".
The following patches are missing explicit approvals:
[01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops
[03/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime
[04/10] gdb: de-constify some methods of solib_ops
[05/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops
[06/10] gdb/solib-rocm: save inferior in rocm_solib_ops
[08/10] gdb: add objfile -> solib backlink
During review of v1, Tom Tromey raised the idea of using multiple
program spaces per inferior to represent the host and device code. I
think it would be a good idea to give it a try, and it might be a
conceptually better idea. But it would also be a significantly larger
and more difficult change, I can't do it in a reasonable time frame, and
there are features coming up waiting for a solution to this problem. I
therefore plan to merge this multiple solib ops series (which isn't
inherently bad) and explore the multiple program space idea separately
without pressure of having to deliver quickly. If that multiple program
space idea ends up working, then the multiple solib ops thing will
become unnecessary, in which case we can remove it.
Below is the original cover letter, which still applies.
This series adds support for having multiple solib_ops instances
providing solibs for a given program_space. The main use case to
justify this change is to make rocm_solib_ops less clunky.
When debugging ROCm programs, we need to have an instance of
svr4_solib_ops providing solibs for host shared libraries, and
an instance of rocm_solib_ops providing solibs for device code objects.
Since there can only be one solib_ops in a program, rocm_solib_ops has
to wrap and replce the svr4_solib_ops instance previously installed.
By allowing multiple solib_ops in the same program_space, the two
solib_ops instances can co-exist without knowing each other.
The main change is contained in the last patch. The patches before that
are cleanup or preparatory patches.
Simon Marchi (10):
gdb/solib-rocm: assert that host ops isn't rocm_solib_ops
gdb/solib-rocm: pass reference to cache to
rocm_code_object_stream_file
gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime
gdb: de-constify some methods of solib_ops
gdb/solib-rocm: move per-inferior data to rocm_solib_ops
gdb/solib-rocm: save inferior in rocm_solib_ops
gdb/solib: add remove_solib function
gdb: add objfile -> solib backlink
gdb: change default objfile iteration order to start with current
objfile
gdb: multiple solib_ops per program space
gdb/amd-dbgapi-target.c | 3 +-
gdb/infcmd.c | 16 +-
gdb/inferior.h | 6 +-
gdb/infrun.c | 7 +-
gdb/objfiles.h | 23 ++
gdb/observable.h | 8 +-
gdb/progspace.c | 91 +++++-
gdb/progspace.h | 57 ++--
gdb/solib-aix.c | 14 +-
gdb/solib-darwin.c | 14 +-
gdb/solib-dsbt.c | 10 +-
gdb/solib-frv.c | 10 +-
gdb/solib-rocm.c | 342 ++++++++++-------------
gdb/solib-svr4.c | 60 ++--
gdb/solib-svr4.h | 20 +-
gdb/solib-target.c | 4 +-
gdb/solib-target.h | 2 +-
gdb/solib.c | 320 ++++++++++++---------
gdb/solib.h | 44 ++-
gdb/target.c | 2 +-
gdb/testsuite/gdb.rocm/symbol-lookup.cpp | 320 +++++++++++++++++++++
gdb/testsuite/gdb.rocm/symbol-lookup.exp | 243 ++++++++++++++++
gdb/windows-tdep.c | 46 +--
23 files changed, 1182 insertions(+), 480 deletions(-)
create mode 100644 gdb/testsuite/gdb.rocm/symbol-lookup.cpp
create mode 100644 gdb/testsuite/gdb.rocm/symbol-lookup.exp
base-commit: 748350ae9e20d4698527c57bb3d3d76a33242cab
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-07-06 17:08 ` [PATCH v2 1/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 02/10] gdb/solib-rocm: pass reference to cache to rocm_code_object_stream_file Simon Marchi
` (8 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
In some fork cases, the rocm_solib_target_inferior_created observer gets
called while the child inferior (passed as a parameter) already has a
rocm_solib_ops installed. Since we unconditionally wrap the existing
solib_ops with a new rocm_solib_ops, we can end up with a chain of
multiple rocm_solib_ops, like:
rocm_solib_ops -> rocm_solib_ops -> svr4_solib_ops
I don't think it is technically harmful as of now (unless the process
does a ton of forks and the rocm_solib_ops accumulate), but it is for
sure useless. Add an assert for this in the rocm_solib_ops constructor,
which reveals the cases where this happens.
When a fork happens with follow-fork-mode == child and detach-on-fork
on, infrun's follow_fork_inferior function directly moves the program
space from the parent the child inferior, as an optimization. Coming
into rocm_solib_target_inferior_created, the inferior's pspace
unexpectedly already has a rocm_solib_ops pushed.
Fix this locally by using an inferior_forked observer. In the scenario
described above, remove the rocm_solib_ops and restore the host
solib_ops as the program space's solib_ops, to make it look as if infrun
didn't do this trick. This requires adding two parameters to the
inferior_forked observer (detach_on_fork and follow_child).
This should probably be done by infrun directly at some point. The
logic being that it's fine to do an optimization, but it should look as
if it didn't occur. If infrun created a brand new pspace for the child,
there wouldn't be a rocm_solib_ops there already. But I prefer a local
fix for now.
Finally, there are also the vfork cases, where the child inferior shares
the program space with its parent, and therefore the child's program
space already has a rocm_solib_ops installed. Address this case by
returning early (the `inf->vfork_parent != nullptr` check), because
there is nothing we want to do for a vfork child anyway.
Change-Id: I2e76d111e96f1e01b6799b04da9cdd6f6e2984c9
---
gdb/amd-dbgapi-target.c | 3 ++-
gdb/infrun.c | 3 ++-
gdb/observable.h | 8 ++++++--
gdb/solib-rocm.c | 31 +++++++++++++++++++++++++++++++
4 files changed, 41 insertions(+), 4 deletions(-)
diff --git a/gdb/amd-dbgapi-target.c b/gdb/amd-dbgapi-target.c
index d44f03d0b80a..6b2314f7de79 100644
--- a/gdb/amd-dbgapi-target.c
+++ b/gdb/amd-dbgapi-target.c
@@ -2335,7 +2335,8 @@ amd_dbgapi_inferior_execd (inferior *exec_inf, inferior *follow_inf)
static void
amd_dbgapi_inferior_forked (inferior *parent_inf, inferior *child_inf,
- target_waitkind fork_kind)
+ target_waitkind fork_kind, bool detach_on_fork,
+ bool follow_child)
{
if (child_inf != nullptr)
{
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 22a056084861..2edd6e0445b3 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -699,7 +699,8 @@ holding the child stopped. Try \"set %ps\" or \"%ps\".\n"),
target_follow_fork (child_inf, child_ptid, fork_kind, follow_child,
detach_fork);
- gdb::observers::inferior_forked.notify (parent_inf, child_inf, fork_kind);
+ gdb::observers::inferior_forked.notify (parent_inf, child_inf, fork_kind,
+ detach_fork, follow_child);
/* target_follow_fork must leave the parent as the current inferior. If we
want to follow the child, we make it the current one below. */
diff --git a/gdb/observable.h b/gdb/observable.h
index 9f1c33ba7100..979e9d1c3ace 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -92,9 +92,13 @@ extern observable<inferior */* exec_inf */, inferior */* follow_inf */>
the child (because we follow only the child or we follow both), CHILD_INF
is the child inferior. Otherwise, CHILD_INF is nullptr.
- FORK_KIND is TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED. */
+ FORK_KIND is TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED.
+
+ DETACH_ON_FORK and FOLLOW_CHILD represent the "detach-on-fork" and
+ "follow-fork-mode" settings. */
extern observable<inferior */* parent_inf */, inferior */* child_inf */,
- target_waitkind /* fork_kind */> inferior_forked;
+ target_waitkind /* fork_kind */, bool /* detach_on_fork */,
+ bool /* follow_child */> inferior_forked;
/* The shared library specified by SOLIB has been loaded. Note that
when gdb calls this observer, the library's symbols probably
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index 34cb2bb9e57c..dcaa0ee980a3 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -165,8 +165,14 @@ struct rocm_solib_ops : public solib_ops
explicit rocm_solib_ops (program_space *pspace, solib_ops_up host_ops)
: solib_ops (pspace), m_host_ops (std::move (host_ops))
{
+ gdb_assert (m_host_ops != nullptr);
+ gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
}
+ /* Release the host solib_ops. */
+ solib_ops_up release_host_ops ()
+ { return std::move (m_host_ops); }
+
/* The methods implemented by rocm_solib_ops. */
owning_intrusive_list<solib> current_sos () const override;
void create_inferior_hook (int from_tty) const override;
@@ -820,6 +826,10 @@ rocm_update_solib_list ()
static void
rocm_solib_target_inferior_created (inferior *inf)
{
+ /* A vfork child shares its pspace with its parent, do not touch anything. */
+ if (inf->vfork_parent != nullptr)
+ return;
+
get_solib_info (inf)->solib_list.clear ();
auto prev_ops = inf->pspace->release_solib_ops ();
@@ -851,6 +861,24 @@ rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf)
get_solib_info (exec_inf)->solib_list.clear ();
}
+static void
+rocm_solib_target_inferior_forked (inferior *parent_inf, inferior *child_inf,
+ target_waitkind fork_kind,
+ bool detach_on_fork, bool follow_child)
+{
+ if (detach_on_fork && follow_child && fork_kind == TARGET_WAITKIND_FORKED)
+ {
+ /* In this particular configuration, infrun's follow_fork_inferior
+ function moves the parent pspace to the child directly. Remove the
+ existing rocm_solib_ops from the child and restore the host solib_ops,
+ to make it look like a brand new pspace. */
+ auto rocm_ops_holder = child_inf->pspace->release_solib_ops ();
+ auto rocm_ops
+ = gdb::checked_static_cast<rocm_solib_ops *> (rocm_ops_holder.get ());
+ child_inf->pspace->set_solib_ops (rocm_ops->release_host_ops ());
+ }
+}
+
INIT_GDB_FILE (rocm_solib)
{
/* The dependency on the amd-dbgapi exists because solib-rocm's
@@ -864,4 +892,7 @@ INIT_GDB_FILE (rocm_solib)
gdb::observers::inferior_execd.attach
(rocm_solib_target_inferior_execd, "solib-rocm",
{ &get_amd_dbgapi_target_inferior_execd_observer_token () });
+
+ gdb::observers::inferior_forked.attach
+ (rocm_solib_target_inferior_forked, "solib-rocm");
}
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 02/10] gdb/solib-rocm: pass reference to cache to rocm_code_object_stream_file
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 03/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime Simon Marchi
` (7 subsequent siblings)
9 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi, Lancelot Six
~rocm_code_object_stream_file obtains the fd cache to call "close" on
using its m_inf field, obtaining the per-inferior info using
get_solib_info. A patch later in this series moves the fd cache from
the per-inferior registry to the rocm_solib_ops directly. This implies
that we will need a new way to get a reference to the fd_cache owning
the fd, as it won't be easy to get it from the inferior anymore.
To achieve this, update rocm_code_object_stream_file to keep a reference
to the fd cache directly (because the rocm_code_object_stream is not
meant to be copied nor moved, make the field a reference). The inferior
parameter and field are not needed anymore.
Change-Id: Ia10f8f125840274e51e188cafcb7384fdff92240
Approved-by: Lancelot Six <lancelot.six@amd.com>
---
gdb/solib-rocm.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index dcaa0ee980a3..f96f0823200e 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -356,8 +356,8 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
{
DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
- rocm_code_object_stream_file (inferior *inf, target_fd fd, ULONGEST offset,
- ULONGEST size);
+ rocm_code_object_stream_file (rocm_solib_fd_cache &fd_cache, target_fd fd,
+ ULONGEST offset, ULONGEST size);
file_ptr read (bfd *abfd, void *buf, file_ptr size,
file_ptr offset) override;
@@ -367,9 +367,8 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
~rocm_code_object_stream_file () override;
protected:
-
- /* The inferior owning this code object stream. */
- inferior *m_inf;
+ /* The fd cache owning this code object stream. */
+ rocm_solib_fd_cache &m_fd_cache;
/* The target file descriptor for this stream. */
target_fd m_fd;
@@ -383,8 +382,9 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
};
rocm_code_object_stream_file::rocm_code_object_stream_file
- (inferior *inf, target_fd fd, ULONGEST offset, ULONGEST size)
- : m_inf (inf), m_fd (fd), m_offset (offset), m_size (size)
+ (rocm_solib_fd_cache &fd_cache, target_fd fd, ULONGEST offset,
+ ULONGEST size)
+ : m_fd_cache (fd_cache), m_fd (fd), m_offset (offset), m_size (size)
{
}
@@ -449,9 +449,8 @@ rocm_code_object_stream_file::size ()
rocm_code_object_stream_file::~rocm_code_object_stream_file ()
{
- auto info = get_solib_info (m_inf);
fileio_error target_errno;
- if (info->fd_cache.close (m_fd, &target_errno) != 0)
+ if (m_fd_cache.close (m_fd, &target_errno) != 0)
warning (_("Failed to close solib: %s"),
strerror (fileio_error_to_host (target_errno)));
}
@@ -620,7 +619,7 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
return nullptr;
}
- return new rocm_code_object_stream_file (inferior, fd, offset,
+ return new rocm_code_object_stream_file (info->fd_cache, fd, offset,
size);
}
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 03/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 02/10] gdb/solib-rocm: pass reference to cache to rocm_code_object_stream_file Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-07-06 17:08 ` [PATCH v2 3/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 04/10] gdb: de-constify some methods of solib_ops Simon Marchi
` (6 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
Make the management of the cached fds automatic.
Change rocm_solib_fd_cache::open to return a cached_target_fd, an RAII
type that calls rocm_solib_fd_cache::close on destruction.
cached_target_fd holds the actual fd and a reference to the cache it
comes from.
Change rocm_code_object_stream_file to hold a cached_target_fd and
remove the explicit destructor.
This is not essential, I just thought it would be nice.
Change-Id: I2529ac1caebbe6b7e6e60d228a403064f6f38d06
---
gdb/solib-rocm.c | 90 +++++++++++++++++++++++++++++++-----------------
1 file changed, 59 insertions(+), 31 deletions(-)
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index f96f0823200e..60e64c494f88 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -40,22 +40,63 @@ struct rocm_solib_fd_cache
explicit rocm_solib_fd_cache (inferior *inf) : m_inferior (inf) {}
DISABLE_COPY_AND_ASSIGN (rocm_solib_fd_cache);
+ /* The open method returns an object of this type.
+
+ On destruction, automatically call close to indicate the caller no longer
+ uses the fd. */
+ struct cached_target_fd
+ {
+ cached_target_fd (target_fd fd, rocm_solib_fd_cache &cache)
+ : m_fd (fd),
+ m_cache (cache)
+ {
+ }
+
+ cached_target_fd (cached_target_fd &&other)
+ : m_fd (other.m_fd),
+ m_cache (other.m_cache)
+ {
+ other.m_fd = target_fd::INVALID;
+ }
+
+ ~cached_target_fd ()
+ {
+ if (m_fd == target_fd::INVALID)
+ return;
+
+ fileio_error target_errno;
+ if (m_cache.close (m_fd, &target_errno) != 0)
+ warning (_("Failed to close solib: %s"),
+ strerror (fileio_error_to_host (target_errno)));
+ }
+
+ DISABLE_COPY_AND_ASSIGN (cached_target_fd);
+
+ target_fd fd () const { return m_fd; }
+
+private:
+ target_fd m_fd;
+ rocm_solib_fd_cache &m_cache;
+ };
+
/* Return a read-only file descriptor to FILENAME and increment the
associated reference count.
Open the file FILENAME if it is not already opened, reuse the existing file
descriptor otherwise.
- On error target_fd::INVALID is returned, and TARGET_ERRNO is set. */
- target_fd open (const std::string &filename, fileio_error *target_errno);
+ On error, return a cached_target_fd with target_fd::INVALID and set
+ *TARGET_ERRNO. */
+ cached_target_fd open (const std::string &filename,
+ fileio_error *target_errno);
+private:
/* Decrement the reference count to FD and close FD if the reference count
reaches 0.
On success, return 0. On error, return -1 and set TARGET_ERRNO. */
int close (target_fd fd, fileio_error *target_errno);
-private:
struct refcnt_fd
{
refcnt_fd (target_fd fd, int refcnt) : fd (fd), refcnt (refcnt) {}
@@ -73,7 +114,7 @@ struct rocm_solib_fd_cache
gdb::unordered_string_map<refcnt_fd> m_cache;
};
-target_fd
+rocm_solib_fd_cache::cached_target_fd
rocm_solib_fd_cache::open (const std::string &filename,
fileio_error *target_errno)
{
@@ -88,7 +129,7 @@ rocm_solib_fd_cache::open (const std::string &filename,
m_cache.emplace (std::piecewise_construct,
std::forward_as_tuple (filename),
std::forward_as_tuple (fd, 1));
- return fd;
+ return cached_target_fd { fd, *this };;
}
else
{
@@ -96,7 +137,7 @@ rocm_solib_fd_cache::open (const std::string &filename,
already opened FD. */
it->second.refcnt++;
gdb_assert (it->second.fd != target_fd::INVALID);
- return it->second.fd;
+ return cached_target_fd { it->second.fd, *this };
}
}
@@ -356,7 +397,7 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
{
DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
- rocm_code_object_stream_file (rocm_solib_fd_cache &fd_cache, target_fd fd,
+ rocm_code_object_stream_file (rocm_solib_fd_cache::cached_target_fd fd,
ULONGEST offset, ULONGEST size);
file_ptr read (bfd *abfd, void *buf, file_ptr size,
@@ -364,14 +405,9 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
LONGEST size () override;
- ~rocm_code_object_stream_file () override;
-
protected:
- /* The fd cache owning this code object stream. */
- rocm_solib_fd_cache &m_fd_cache;
-
/* The target file descriptor for this stream. */
- target_fd m_fd;
+ rocm_solib_fd_cache::cached_target_fd m_fd;
/* The offset of the ELF file image in the target file. */
ULONGEST m_offset;
@@ -382,9 +418,8 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
};
rocm_code_object_stream_file::rocm_code_object_stream_file
- (rocm_solib_fd_cache &fd_cache, target_fd fd, ULONGEST offset,
- ULONGEST size)
- : m_fd_cache (fd_cache), m_fd (fd), m_offset (offset), m_size (size)
+ (rocm_solib_fd_cache::cached_target_fd fd, ULONGEST offset, ULONGEST size)
+ : m_fd (std::move (fd)), m_offset (offset), m_size (size)
{
}
@@ -399,9 +434,9 @@ rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
QUIT;
file_ptr bytes_read
- = target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
- size, m_offset + offset + nbytes,
- &target_errno);
+ = target_fileio_pread (m_fd.fd (),
+ static_cast<gdb_byte *> (buf) + nbytes, size,
+ m_offset + offset + nbytes, &target_errno);
if (bytes_read == 0)
break;
@@ -427,7 +462,7 @@ rocm_code_object_stream_file::size ()
{
fileio_error target_errno;
struct stat stat;
- if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
+ if (target_fileio_fstat (m_fd.fd (), &stat, &target_errno) < 0)
{
errno = fileio_error_to_host (target_errno);
bfd_set_error (bfd_error_system_call);
@@ -447,14 +482,6 @@ rocm_code_object_stream_file::size ()
return m_size;
}
-rocm_code_object_stream_file::~rocm_code_object_stream_file ()
-{
- fileio_error target_errno;
- if (m_fd_cache.close (m_fd, &target_errno) != 0)
- warning (_("Failed to close solib: %s"),
- strerror (fileio_error_to_host (target_errno)));
-}
-
/* Interface to a code object which lives in the inferior's memory. */
struct rocm_code_object_stream_memory final : public rocm_code_object_stream
@@ -610,16 +637,17 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
auto info = get_solib_info (inferior);
fileio_error target_errno;
- target_fd fd = info->fd_cache.open (decoded_path, &target_errno);
+ rocm_solib_fd_cache::cached_target_fd fd
+ = info->fd_cache.open (decoded_path, &target_errno);
- if (fd == target_fd::INVALID)
+ if (fd.fd () == target_fd::INVALID)
{
errno = fileio_error_to_host (target_errno);
bfd_set_error (bfd_error_system_call);
return nullptr;
}
- return new rocm_code_object_stream_file (info->fd_cache, fd, offset,
+ return new rocm_code_object_stream_file (std::move (fd), offset,
size);
}
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 04/10] gdb: de-constify some methods of solib_ops
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (2 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 03/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-07-06 17:09 ` [PATCH v2 4/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 05/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops Simon Marchi
` (5 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
The following commit moves the ROCm solib data from a registry to the
rocm_solib_ops object itself. This requires making some methods
non-const, and it then cascades to a bunch of things. This will have to
be done one day or another, when (I suspect) we'll move more solib_ops
data out of registries and into the solib_ops objects themselves. Do it
in this separate patch to avoid polluting that patch.
No behavior change expected.
Change-Id: Icb5e921a82e235817f8817a3aa3c20b2c9dc959f
---
gdb/progspace.h | 2 +-
gdb/solib-aix.c | 12 ++++++------
gdb/solib-darwin.c | 12 ++++++------
gdb/solib-dsbt.c | 8 ++++----
gdb/solib-frv.c | 8 ++++----
gdb/solib-rocm.c | 20 ++++++++++----------
gdb/solib-svr4.c | 14 +++++++-------
gdb/solib-svr4.h | 14 +++++++-------
gdb/solib-target.c | 2 +-
gdb/solib-target.h | 2 +-
gdb/solib.c | 8 ++++----
gdb/solib.h | 14 +++++++-------
gdb/windows-tdep.c | 4 ++--
13 files changed, 60 insertions(+), 60 deletions(-)
diff --git a/gdb/progspace.h b/gdb/progspace.h
index e9261ff8590d..21977747cb94 100644
--- a/gdb/progspace.h
+++ b/gdb/progspace.h
@@ -260,7 +260,7 @@ struct program_space
{ return std::move (m_solib_ops); }
/* Get this program space's solib provider. */
- const struct solib_ops *solib_ops () const
+ struct solib_ops *solib_ops () const
{ return m_solib_ops.get (); }
/* Return the list of all the solibs in this program space. */
diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c
index 705760651217..28f69ed9cbb4 100644
--- a/gdb/solib-aix.c
+++ b/gdb/solib-aix.c
@@ -32,9 +32,9 @@ struct aix_solib_ops : public solib_ops
using solib_ops::solib_ops;
void relocate_section_addresses (solib &so, target_section *) const override;
- void create_inferior_hook (int from_tty) const override;
- owning_intrusive_list<solib> current_sos () const override;
- gdb_bfd_ref_ptr bfd_open (const char *pathname) 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. */
@@ -425,7 +425,7 @@ solib_aix_get_section_offsets (struct objfile *objfile,
}
void
-aix_solib_ops::create_inferior_hook (int from_tty) const
+aix_solib_ops::create_inferior_hook (int from_tty)
{
const char *warning_msg = "unable to relocate main executable";
@@ -454,7 +454,7 @@ aix_solib_ops::create_inferior_hook (int from_tty) const
}
owning_intrusive_list<solib>
-aix_solib_ops::current_sos () const
+aix_solib_ops::current_sos ()
{
std::optional<std::vector<lm_info_aix>> &library_list
= solib_aix_get_library_list (current_inferior (), NULL);
@@ -498,7 +498,7 @@ aix_solib_ops::current_sos () const
}
gdb_bfd_ref_ptr
-aix_solib_ops::bfd_open (const char *pathname) const
+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).
diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c
index 6e2535ca427f..07cd88d0e0c6 100644
--- a/gdb/solib-darwin.c
+++ b/gdb/solib-darwin.c
@@ -42,9 +42,9 @@ struct darwin_solib_ops : public solib_ops
void relocate_section_addresses (solib &so, target_section *) const override;
void clear_solib (program_space *pspace) const override;
- void create_inferior_hook (int from_tty) const override;
- owning_intrusive_list<solib> current_sos () const override;
- gdb_bfd_ref_ptr bfd_open (const char *pathname) 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-darwin.h. */
@@ -211,7 +211,7 @@ find_program_interpreter (void)
}
owning_intrusive_list<solib>
-darwin_solib_ops::current_sos () const
+darwin_solib_ops::current_sos ()
{
type *ptr_type
= builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
@@ -471,7 +471,7 @@ darwin_solib_read_all_image_info_addr (struct darwin_info *info)
}
void
-darwin_solib_ops::create_inferior_hook (int from_tty) const
+darwin_solib_ops::create_inferior_hook (int from_tty)
{
/* Everything below only makes sense if we have a running inferior. */
if (!target_has_execution ())
@@ -606,7 +606,7 @@ darwin_solib_ops::relocate_section_addresses (solib &so,
}
gdb_bfd_ref_ptr
-darwin_solib_ops::bfd_open (const char *pathname) const
+darwin_solib_ops::bfd_open (const char *pathname)
{
int found_file;
diff --git a/gdb/solib-dsbt.c b/gdb/solib-dsbt.c
index 052b170fd9a6..22b9a581546a 100644
--- a/gdb/solib-dsbt.c
+++ b/gdb/solib-dsbt.c
@@ -128,8 +128,8 @@ struct dsbt_solib_ops : public solib_ops
void relocate_section_addresses (solib &so, target_section *) const override;
void clear_solib (program_space *pspace) const override;
- void create_inferior_hook (int from_tty) const override;
- owning_intrusive_list<solib> current_sos () const 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;
};
@@ -526,7 +526,7 @@ lm_base (void)
we provide values for. */
owning_intrusive_list<solib>
-dsbt_solib_ops::current_sos () const
+dsbt_solib_ops::current_sos ()
{
bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ());
CORE_ADDR lm_addr;
@@ -860,7 +860,7 @@ dsbt_relocate_main_executable (void)
The shared library breakpoints also need to be enabled. */
void
-dsbt_solib_ops::create_inferior_hook (int from_tty) const
+dsbt_solib_ops::create_inferior_hook (int from_tty)
{
/* Relocate main executable. */
dsbt_relocate_main_executable ();
diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c
index 4f0aac31e733..cff3c3ca0193 100644
--- a/gdb/solib-frv.c
+++ b/gdb/solib-frv.c
@@ -36,8 +36,8 @@ struct frv_solib_ops : public solib_ops
void relocate_section_addresses (solib &so, target_section *) const override;
void clear_solib (program_space *pspace) const override;
- void create_inferior_hook (int from_tty) const override;
- owning_intrusive_list<solib> current_sos () const 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;
};
@@ -321,7 +321,7 @@ lm_base (void)
}
owning_intrusive_list<solib>
-frv_solib_ops::current_sos () const
+frv_solib_ops::current_sos ()
{
bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ());
CORE_ADDR lm_addr, mgot;
@@ -789,7 +789,7 @@ frv_relocate_main_executable (void)
enabled. */
void
-frv_solib_ops::create_inferior_hook (int from_tty) const
+frv_solib_ops::create_inferior_hook (int from_tty)
{
/* Relocate main executable. */
frv_relocate_main_executable ();
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index 60e64c494f88..4032ada28a0f 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -215,11 +215,11 @@ struct rocm_solib_ops : public solib_ops
{ return std::move (m_host_ops); }
/* The methods implemented by rocm_solib_ops. */
- owning_intrusive_list<solib> current_sos () const override;
- void create_inferior_hook (int from_tty) const override;
- gdb_bfd_ref_ptr bfd_open (const char *pathname) const override;
+ 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 () 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
@@ -270,7 +270,7 @@ struct rocm_solib_ops : public solib_ops
private:
owning_intrusive_list<solib>
- solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const;
+ solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
solib_ops_up m_host_ops;
};
@@ -303,7 +303,7 @@ rocm_solib_ops::relocate_section_addresses (solib &so,
static void rocm_update_solib_list ();
void
-rocm_solib_ops::handle_event () const
+rocm_solib_ops::handle_event ()
{
/* 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
@@ -319,7 +319,7 @@ rocm_solib_ops::handle_event () const
/* Create solib objects from rocm_so objects in SOS. */
owning_intrusive_list<solib>
-rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const
+rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
{
owning_intrusive_list<solib> dst;
@@ -334,7 +334,7 @@ rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const
objects currently loaded in the inferior. */
owning_intrusive_list<solib>
-rocm_solib_ops::current_sos () const
+rocm_solib_ops::current_sos ()
{
/* First, retrieve the host-side shared library list. */
owning_intrusive_list<solib> sos = m_host_ops->current_sos ();
@@ -692,7 +692,7 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
}
gdb_bfd_ref_ptr
-rocm_solib_ops::bfd_open (const char *pathname) const
+rocm_solib_ops::bfd_open (const char *pathname)
{
/* Handle regular files with SVR4 open. */
if (strstr (pathname, "://") == nullptr)
@@ -780,7 +780,7 @@ rocm_solib_ops::bfd_open (const char *pathname) const
}
void
-rocm_solib_ops::create_inferior_hook (int from_tty) const
+rocm_solib_ops::create_inferior_hook (int from_tty)
{
get_solib_info (current_inferior ())->solib_list.clear ();
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 8e3de4d3ea1a..35c81000b10a 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -1059,7 +1059,7 @@ svr4_solib_ops::clear_so (const solib &so) const
/* Create the solib objects equivalent to the svr4_sos in SOS. */
owning_intrusive_list<solib>
-svr4_solib_ops::solibs_from_svr4_sos (const std::vector<svr4_so> &sos) const
+svr4_solib_ops::solibs_from_svr4_sos (const std::vector<svr4_so> &sos)
{
owning_intrusive_list<solib> dst;
@@ -1253,7 +1253,7 @@ svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list,
linker, build a fallback list from other sources. */
owning_intrusive_list<solib>
-svr4_solib_ops::default_sos (svr4_info *info) const
+svr4_solib_ops::default_sos (svr4_info *info)
{
if (!info->debug_loader_offset_p)
return {};
@@ -1452,7 +1452,7 @@ svr4_solib_ops::current_sos_direct (svr4_info *info) const
/* Collect sos read and stored by the probes interface. */
owning_intrusive_list<solib>
-svr4_solib_ops::collect_probes_sos (svr4_info *info) const
+svr4_solib_ops::collect_probes_sos (svr4_info *info)
{
owning_intrusive_list<solib> res;
@@ -1469,7 +1469,7 @@ svr4_solib_ops::collect_probes_sos (svr4_info *info) const
method. */
owning_intrusive_list<solib>
-svr4_solib_ops::current_sos_1 (svr4_info *info) const
+svr4_solib_ops::current_sos_1 (svr4_info *info)
{
owning_intrusive_list<solib> sos;
@@ -1496,7 +1496,7 @@ svr4_solib_ops::current_sos_1 (svr4_info *info) const
/* Implement the "current_sos" solib_ops method. */
owning_intrusive_list<solib>
-svr4_solib_ops::current_sos () const
+svr4_solib_ops::current_sos ()
{
svr4_info *info = get_svr4_info (current_program_space);
@@ -2143,7 +2143,7 @@ svr4_solib_ops::disable_probes_interface (svr4_info *info) const
standard interface. */
void
-svr4_solib_ops::handle_event () const
+svr4_solib_ops::handle_event ()
{
struct svr4_info *info = get_svr4_info (current_program_space);
struct probe_and_action *pa;
@@ -3318,7 +3318,7 @@ svr4_relocate_main_executable (void)
their symbols to be read at a later time. */
void
-svr4_solib_ops::create_inferior_hook (int from_tty) const
+svr4_solib_ops::create_inferior_hook (int from_tty)
{
struct svr4_info *info;
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index 3078a092778c..d7f58a76224d 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -99,14 +99,14 @@ 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) const override;
- void create_inferior_hook (int from_tty) const override;
- owning_intrusive_list<solib> current_sos () const override;
+ void create_inferior_hook (int from_tty) override;
+ owning_intrusive_list<solib> current_sos () override;
bool open_symbol_file_object (int from_tty) const 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) const override;
void update_breakpoints () const override;
- void handle_event () 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;
@@ -135,7 +135,7 @@ struct svr4_solib_ops : public solib_ops
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) const;
+ owning_intrusive_list<solib> default_sos (svr4_info *info);
int read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
CORE_ADDR debug_base, std::vector<svr4_so> &sos,
int ignore_first) const;
@@ -144,10 +144,10 @@ struct svr4_solib_ops : public solib_ops
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;
- owning_intrusive_list<solib> collect_probes_sos (svr4_info *info) const;
- owning_intrusive_list<solib> current_sos_1 (svr4_info *info) 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) const;
+ (const std::vector<svr4_so> &sos);
void disable_probes_interface (svr4_info *info) const;
void update_full (svr4_info *info) const;
int update_incremental (svr4_info *info, CORE_ADDR debug_base,
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index ac965df20e94..0df8c6048aef 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -232,7 +232,7 @@ solib_target_parse_libraries (const char *library)
#endif
owning_intrusive_list<solib>
-target_solib_ops::current_sos () const
+target_solib_ops::current_sos ()
{
owning_intrusive_list<solib> sos;
diff --git a/gdb/solib-target.h b/gdb/solib-target.h
index 0d2f0840aa63..abfa0b513742 100644
--- a/gdb/solib-target.h
+++ b/gdb/solib-target.h
@@ -29,7 +29,7 @@ struct target_solib_ops : solib_ops
using solib_ops::solib_ops;
void relocate_section_addresses (solib &so, target_section *) const override;
- owning_intrusive_list<solib> current_sos () const override;
+ owning_intrusive_list<solib> current_sos () override;
bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
};
diff --git a/gdb/solib.c b/gdb/solib.c
index d92d0dd9a3dd..ae41572b2b58 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -438,7 +438,7 @@ solib_bfd_open (const char *pathname)
}
gdb_bfd_ref_ptr
-solib_ops::bfd_open (const char *pathname) const
+solib_ops::bfd_open (const char *pathname)
{
return solib_bfd_open (pathname);
}
@@ -689,7 +689,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;
@@ -1289,7 +1289,7 @@ clear_solib (program_space *pspace)
void
solib_create_inferior_hook (int from_tty)
{
- if (const solib_ops *ops = current_program_space->solib_ops ();
+ if (solib_ops *ops = current_program_space->solib_ops ();
ops != nullptr)
ops->create_inferior_hook (from_tty);
}
@@ -1354,7 +1354,7 @@ update_solib_breakpoints (void)
void
handle_solib_event (void)
{
- if (const solib_ops *ops = current_program_space->solib_ops ();
+ if (solib_ops *ops = current_program_space->solib_ops ();
ops != nullptr)
ops->handle_event ();
diff --git a/gdb/solib.h b/gdb/solib.h
index 9e6c3f7346ee..5b5049f53435 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -62,7 +62,7 @@ struct solib : intrusive_list_node<solib>
OPS is the solib_ops implementation providing this solib. */
explicit solib (lm_info_up lm_info, std::string original_name,
- std::string name, const solib_ops &ops)
+ std::string name, solib_ops &ops)
: lm_info (std::move (lm_info)),
original_name (std::move (original_name)),
name (std::move (name)),
@@ -70,7 +70,7 @@ struct solib : intrusive_list_node<solib>
{}
/* 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
@@ -131,7 +131,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. */
@@ -173,7 +173,7 @@ struct solib_ops
/* Target dependent code to run after child process fork.
Defaults to no-op. */
- virtual void create_inferior_hook (int from_tty) const {};
+ 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.
@@ -182,7 +182,7 @@ 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. */
- virtual owning_intrusive_list<solib> current_sos () const = 0;
+ 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.
@@ -198,7 +198,7 @@ struct solib_ops
{ return false; };
/* Find and open shared library binary file. */
- virtual gdb_bfd_ref_ptr bfd_open (const char *pathname) const;
+ virtual gdb_bfd_ref_ptr bfd_open (const char *pathname);
/* 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.
@@ -226,7 +226,7 @@ struct solib_ops
solib_add is called.
Defaults to no-op. */
- virtual void handle_event () const {};
+ 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
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index 78bf49b9c81f..261c38b35635 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -828,7 +828,7 @@ struct windows_solib_ops : target_solib_ops
{
using target_solib_ops::target_solib_ops;
- void create_inferior_hook (int from_tty) const override;
+ void create_inferior_hook (int from_tty) override;
void iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const override;
@@ -845,7 +845,7 @@ make_windows_solib_ops (program_space *pspace)
/* Implement the "solib_create_inferior_hook" solib_ops method. */
void
-windows_solib_ops::create_inferior_hook (int from_tty) const
+windows_solib_ops::create_inferior_hook (int from_tty)
{
CORE_ADDR exec_base = 0;
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 05/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (3 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 04/10] gdb: de-constify some methods of solib_ops Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-07-06 17:09 ` [PATCH v2 5/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 06/10] gdb/solib-rocm: save inferior in rocm_solib_ops Simon Marchi
` (4 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
Move the data currently stored in an inferior registry directly into
rocm_solib_ops. This changes the storage of this data from "per
inferior" to "per program-space", but it should be equivalent, since
there is usually a 1:1 mapping between those two. The only time there
isn't a 1:1 mapping is during a vfork, before the exec/exit, but no ROCm
activity happens during that slice of time.
Function rocm_update_solib_list becomes
rocm_solib_ops::update_solib_list.
Function rocm_bfd_iovec_open becomes rocm_solib_ops::bfd_iovec_open.
Most of the changes consist of accessing the fields directly, instead of
going through the solib_info object.
The rocm_solib_ops constructor has to take an inferior as a parameter,
rather than a program_space, because of the fd cache.
Change-Id: I386139008b6f0eca1d897fc35be18faa9d5a104e
---
gdb/solib-rocm.c | 90 ++++++++++++++++++------------------------------
1 file changed, 33 insertions(+), 57 deletions(-)
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index 4032ada28a0f..618f4f5e7905 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -181,30 +181,15 @@ struct rocm_so
lm_info_svr4_up lm_info;
};
-struct solib_info
-{
- explicit solib_info (inferior *inf)
- : fd_cache (inf)
- {};
-
- /* List of code objects loaded into the inferior. */
- std::vector<rocm_so> solib_list;
-
- /* Cache of opened FD in the inferior. */
- rocm_solib_fd_cache fd_cache;
-};
-
-/* Per-inferior data key. */
-static const registry<inferior>::key<solib_info> rocm_solib_data;
-
/* 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 (program_space *pspace, solib_ops_up host_ops)
- : solib_ops (pspace), m_host_ops (std::move (host_ops))
+ explicit rocm_solib_ops (inferior *inf, solib_ops_up host_ops)
+ : solib_ops (inf->pspace), m_host_ops (std::move (host_ops)),
+ m_fd_cache (inf)
{
gdb_assert (m_host_ops != nullptr);
gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
@@ -221,6 +206,10 @@ struct rocm_solib_ops : public solib_ops
void relocate_section_addresses (solib &so, target_section *) const override;
void handle_event () override;
+ /* This needs to be public because it is called from
+ rocm_solib_target_inferior_created. */
+ void update_solib_list ();
+
/* 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. */
@@ -271,18 +260,17 @@ struct rocm_solib_ops : public solib_ops
private:
owning_intrusive_list<solib>
solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
+ gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd, inferior *inferior);
solib_ops_up m_host_ops;
+
+ /* List of code objects loaded into the inferior. */
+ std::vector<rocm_so> m_solib_list;
+
+ /* Cache of opened FD in the inferior. */
+ rocm_solib_fd_cache m_fd_cache;
};
-/* Fetch the solib_info data for INF. */
-
-static struct solib_info *
-get_solib_info (inferior *inf)
-{
- return &rocm_solib_data.try_emplace (inf, inf);
-}
-
/* Relocate section addresses. */
void
@@ -300,8 +288,6 @@ rocm_solib_ops::relocate_section_addresses (solib &so,
sec->endaddr = sec->endaddr + li->l_addr;
}
-static void rocm_update_solib_list ();
-
void
rocm_solib_ops::handle_event ()
{
@@ -313,7 +299,7 @@ rocm_solib_ops::handle_event ()
previously loaded). */
m_host_ops->handle_event ();
- rocm_update_solib_list ();
+ this->update_solib_list ();
}
/* Create solib objects from rocm_so objects in SOS. */
@@ -340,12 +326,10 @@ rocm_solib_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;
-
- if (dev_sos.empty ())
+ if (m_solib_list.empty ())
return sos;
- owning_intrusive_list<solib> dev_solibs = solibs_from_rocm_sos (dev_sos);
+ owning_intrusive_list<solib> dev_solibs = solibs_from_rocm_sos (m_solib_list);
if (sos.empty ())
return dev_solibs;
@@ -525,8 +509,8 @@ rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
} /* anonymous namespace */
-static gdb_bfd_iovec_base *
-rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
+gdb_bfd_iovec_base *
+rocm_solib_ops::bfd_iovec_open (bfd *abfd, inferior *inferior)
{
std::string_view uri (bfd_get_filename (abfd));
std::string_view protocol_delim = "://";
@@ -635,10 +619,9 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
&& decoded_path[3] == '/')
decoded_path.erase (0, 1);
- auto info = get_solib_info (inferior);
fileio_error target_errno;
rocm_solib_fd_cache::cached_target_fd fd
- = info->fd_cache.open (decoded_path, &target_errno);
+ = m_fd_cache.open (decoded_path, &target_errno);
if (fd.fd () == target_fd::INVALID)
{
@@ -698,9 +681,9 @@ rocm_solib_ops::bfd_open (const char *pathname)
if (strstr (pathname, "://") == nullptr)
return m_host_ops->bfd_open (pathname);
- auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
+ auto open = [this] (bfd *nbfd)
{
- return rocm_bfd_iovec_open (nbfd, current_inferior ());
+ return this->bfd_iovec_open (nbfd, current_inferior ());
};
gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
@@ -782,13 +765,12 @@ rocm_solib_ops::bfd_open (const char *pathname)
void
rocm_solib_ops::create_inferior_hook (int from_tty)
{
- get_solib_info (current_inferior ())->solib_list.clear ();
-
+ m_solib_list.clear ();
m_host_ops->create_inferior_hook (from_tty);
}
-static void
-rocm_update_solib_list ()
+void
+rocm_solib_ops::update_solib_list ()
{
inferior *inf = current_inferior ();
@@ -796,10 +778,7 @@ rocm_update_solib_list ()
if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
return;
- solib_info *info = get_solib_info (inf);
-
- info->solib_list.clear ();
- std::vector<rocm_so> &sos = info->solib_list;
+ m_solib_list.clear ();
amd_dbgapi_code_object_id_t *code_object_list;
size_t count;
@@ -846,7 +825,8 @@ rocm_update_solib_list ()
std::string unique_name
= string_printf ("code_object_%ld", code_object_list[i].handle);
- sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
+ m_solib_list.emplace_back (uri_bytes, std::move (unique_name),
+ std::move (li));
}
}
@@ -857,17 +837,15 @@ rocm_solib_target_inferior_created (inferior *inf)
if (inf->vfork_parent != nullptr)
return;
- get_solib_info (inf)->solib_list.clear ();
-
auto prev_ops = inf->pspace->release_solib_ops ();
- auto rocm_ops
- = std::make_unique<rocm_solib_ops> (inf->pspace, std::move (prev_ops));
+ auto rocm_ops = std::make_unique<rocm_solib_ops> (inf, std::move (prev_ops));
inf->pspace->set_solib_ops (std::move (rocm_ops));
- rocm_update_solib_list ();
+ gdb::checked_static_cast<rocm_solib_ops *>
+ (inf->pspace->solib_ops ())->update_solib_list ();
/* Force GDB to reload the solibs. */
- current_inferior ()->pspace->clear_solib_cache ();
+ inf->pspace->clear_solib_cache ();
solib_add (nullptr, 0, auto_solib_add);
}
@@ -882,10 +860,8 @@ rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf)
auto pspace = follow_inf->pspace;
auto prev_ops = pspace->release_solib_ops ();
auto rocm_ops
- = std::make_unique<rocm_solib_ops> (pspace, std::move (prev_ops));
+ = std::make_unique<rocm_solib_ops> (follow_inf, std::move (prev_ops));
pspace->set_solib_ops (std::move (rocm_ops));
-
- get_solib_info (exec_inf)->solib_list.clear ();
}
static void
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 06/10] gdb/solib-rocm: save inferior in rocm_solib_ops
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (4 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 05/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-07-06 17:10 ` [PATCH v2 6/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 07/10] gdb/solib: add remove_solib function Simon Marchi
` (3 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Storing the inferior in rocm_solib_ops lets us avoid some calls to
current_inferior().
Change-Id: Icf36bbef031eda7b776cd735a91a2ff918697dd3
---
gdb/solib-rocm.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index 618f4f5e7905..0a284dfb0a56 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -189,7 +189,7 @@ struct rocm_solib_ops : public solib_ops
in order to provide support for ROCm code objects. */
explicit rocm_solib_ops (inferior *inf, solib_ops_up host_ops)
: solib_ops (inf->pspace), m_host_ops (std::move (host_ops)),
- m_fd_cache (inf)
+ m_inf (inf), m_fd_cache (inf)
{
gdb_assert (m_host_ops != nullptr);
gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
@@ -260,10 +260,13 @@ struct rocm_solib_ops : public solib_ops
private:
owning_intrusive_list<solib>
solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
- gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd, inferior *inferior);
+ gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd);
solib_ops_up m_host_ops;
+ /* Inferior this rocm_solib_ops is for. */
+ inferior *m_inf;
+
/* List of code objects loaded into the inferior. */
std::vector<rocm_so> m_solib_list;
@@ -510,7 +513,7 @@ rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
} /* anonymous namespace */
gdb_bfd_iovec_base *
-rocm_solib_ops::bfd_iovec_open (bfd *abfd, inferior *inferior)
+rocm_solib_ops::bfd_iovec_open (bfd *abfd)
{
std::string_view uri (bfd_get_filename (abfd));
std::string_view protocol_delim = "://";
@@ -637,7 +640,7 @@ rocm_solib_ops::bfd_iovec_open (bfd *abfd, inferior *inferior)
if (protocol == "memory")
{
ULONGEST pid = try_strtoulst (path);
- if (pid != inferior->pid)
+ if (pid != m_inf->pid)
{
warning (_("`%s': code object is from another inferior"),
std::string (uri).c_str ());
@@ -683,7 +686,7 @@ rocm_solib_ops::bfd_open (const char *pathname)
auto open = [this] (bfd *nbfd)
{
- return this->bfd_iovec_open (nbfd, current_inferior ());
+ return this->bfd_iovec_open (nbfd);
};
gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
@@ -772,9 +775,7 @@ rocm_solib_ops::create_inferior_hook (int from_tty)
void
rocm_solib_ops::update_solib_list ()
{
- inferior *inf = current_inferior ();
-
- amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
+ amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (m_inf);
if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
return;
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 07/10] gdb/solib: add remove_solib function
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (5 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 06/10] gdb/solib-rocm: save inferior in rocm_solib_ops Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 08/10] gdb: add objfile -> solib backlink Simon Marchi
` (2 subsequent siblings)
9 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi, Tom Tromey
From: Simon Marchi <simon.marchi@polymtl.ca>
Factor out the code to remove an solib into a new remove_solib function.
This function is used by a patch later in this series.
This patch only moves code, so no behavior change expected.
Change-Id: I6bb901c954cdcf900a3f09a4c4d1ba44203f1ed0
Approved-By: Tom Tromey <tom@tromey.com>
---
gdb/solib.c | 58 +++++++++++++++++++++++++++++++----------------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/gdb/solib.c b/gdb/solib.c
index ae41572b2b58..1211874d22be 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -684,6 +684,39 @@ notify_solib_unloaded (program_space *pspace, const solib &so,
gdb::observers::solib_unloaded.notify (pspace, so, still_in_use, silent);
}
+/* Remove solib *SOLIB_IT from PSPACE.
+
+ Remove the corresponding objfiles and target sections.
+
+ Return an iterator to the next solib in PSPACE's solib list, helping to
+ continue iterating. */
+
+static owning_intrusive_list<solib>::iterator
+remove_solib (program_space *pspace,
+ owning_intrusive_list<solib>::iterator solib_it)
+{
+ bool still_in_use
+ = solib_it->objfile != nullptr && solib_used (pspace, *solib_it);
+
+ /* Notify any observer that the shared object has been unloaded before we
+ remove it from GDB's tables. */
+ notify_solib_unloaded (pspace, *solib_it, still_in_use, false);
+
+ /* Unless the user loaded it explicitly, free SO's objfile. */
+ if (solib_it->objfile != nullptr
+ && !(solib_it->objfile->flags & OBJF_USERLOADED)
+ && !still_in_use)
+ solib_it->objfile->unlink ();
+
+ pspace->deleted_solibs.push_back (solib_it->name);
+
+ /* Some targets' section tables might be referring to
+ sections from so.abfd; remove them. */
+ pspace->remove_target_sections (&*solib_it);
+
+ return pspace->solibs ().erase (solib_it);
+}
+
/* See solib.h. */
void
@@ -767,30 +800,7 @@ update_solib_list (int from_tty)
/* If it's not on the inferior's list, remove it from GDB's tables. */
else
- {
- bool still_in_use
- = (gdb_iter->objfile != nullptr
- && solib_used (current_program_space, *gdb_iter));
-
- /* Notify any observer that the shared object has been
- unloaded before we remove it from GDB's tables. */
- notify_solib_unloaded (current_program_space, *gdb_iter,
- still_in_use, false);
-
- /* Unless the user loaded it explicitly, free SO's objfile. */
- if (gdb_iter->objfile != nullptr
- && !(gdb_iter->objfile->flags & OBJF_USERLOADED)
- && !still_in_use)
- gdb_iter->objfile->unlink ();
-
- current_program_space->deleted_solibs.push_back (gdb_iter->name);
-
- /* Some targets' section tables might be referring to
- sections from so.abfd; remove them. */
- current_program_space->remove_target_sections (&*gdb_iter);
-
- gdb_iter = current_program_space->solibs ().erase (gdb_iter);
- }
+ gdb_iter = remove_solib (current_program_space, gdb_iter);
}
/* Now the inferior's list contains only shared objects that don't
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 08/10] gdb: add objfile -> solib backlink
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (6 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 07/10] gdb/solib: add remove_solib function Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 09/10] gdb: change default objfile iteration order to start with current objfile Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 10/10] gdb: multiple solib_ops per program space Simon Marchi
9 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
The following "multiple solib_ops" patch will require accessing the
solib(s) related to an objfile fairly often. To make that easier, add
an objfile to solib backlink.
However, I learned that there may be more than one solib linked to one
objfile. The only case I am aware of is the dynamic linker itself
(ld-linux.so), where the same instance (same load address) appears in
all namespaces. We get one solib per "view" of that library, but they
all point to the same objfile. So this backlink is actually a vector.
Rename find_solib_for_objfile to find_one_solib_for_objfile to reflect
that this function returns just one of possible multiple solibs for the
given objfile (if that ever matters). To be clear, this doesn't change
its behavior, this is what it was doing before this patch.
Change-Id: I427d19407fdbc274b1790e0cae3004ee5fdbea72
---
gdb/objfiles.h | 23 +++++++++++++++++++++++
gdb/solib-svr4.c | 16 ++++++++--------
gdb/solib.c | 13 +++++++++----
3 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index c8b989911e5f..9bd676f27237 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -450,6 +450,25 @@ struct objfile : intrusive_list_node<objfile>
/* Return the program space associated with this objfile. */
program_space *pspace () { return m_pspace; }
+ /* Return the list of struct solib associated to this objfile. */
+ std::vector<solib *> &solibs () { return m_solibs; }
+
+ /* Add SOLIB to the list of solibs associated to this objfile. */
+ void add_solib (solib &solib)
+ {
+ gdb_assert (std::find (m_solibs.begin (), m_solibs.end (), &solib)
+ == m_solibs.end ());
+ m_solibs.emplace_back (&solib);
+ }
+
+ /* Remove SOLIB from the list of solibs associated to this objfile. */
+ void remove_solib (solib &solib)
+ {
+ auto it = std::find (m_solibs.begin (), m_solibs.end (), &solib);
+ gdb_assert (it != m_solibs.end ());
+ m_solibs.erase (it);
+ }
+
using compunit_symtab_iterator
= owning_intrusive_list<compunit_symtab>::iterator;
using compunit_symtab_range = iterator_range<compunit_symtab_iterator>;
@@ -702,6 +721,10 @@ struct objfile : intrusive_list_node<objfile>
program_space *m_pspace;
+ /* The solibs associated to this objfile. This is a 1 objfile to N solibs
+ relationship (see solib.c for details). */
+ std::vector<solib *> m_solibs;
+
public:
/* The object file's BFD. Can be null if the objfile contains only
minimal symbols (e.g. the run time common symbols for SunOS4) or
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 35c81000b10a..33fb6e3cd970 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -3592,10 +3592,10 @@ lp64_svr4_solib_ops::fetch_link_map_offsets () const
}
\f
-/* Return the DSO matching OBJFILE or nullptr if none can be found. */
+/* Return one solib matching OBJFILE or nullptr if none can be found. */
-static const solib *
-find_solib_for_objfile (struct objfile *objfile)
+static solib *
+find_one_solib_for_objfile (struct objfile *objfile)
{
if (objfile == nullptr)
return nullptr;
@@ -3605,9 +3605,9 @@ find_solib_for_objfile (struct objfile *objfile)
if (objfile->separate_debug_objfile_backlink != nullptr)
objfile = objfile->separate_debug_objfile_backlink;
- for (const solib &so : current_program_space->solibs ())
- if (so.objfile == objfile)
- return &so;
+ if (const auto &solibs = objfile->solibs ();
+ !solibs.empty ())
+ return solibs.front ();
return nullptr;
}
@@ -3676,7 +3676,7 @@ svr4_solib_ops::iterate_over_objfiles_in_search_order
r_debug object, defaulting to the initial namespace. */
svr4_info *info = get_svr4_info (current_program_space);
CORE_ADDR default_debug_base = this->default_debug_base (info);
- const solib *curr_solib = find_solib_for_objfile (current_objfile);
+ const solib *curr_solib = find_one_solib_for_objfile (current_objfile);
CORE_ADDR debug_base = find_debug_base_for_solib (curr_solib);
if (debug_base == 0)
debug_base = default_debug_base;
@@ -3691,7 +3691,7 @@ svr4_solib_ops::iterate_over_objfiles_in_search_order
If we fail, e.g. for manually added symbol files or for the main
executable, we assume that they were added to the initial
namespace. */
- const solib *solib = find_solib_for_objfile (&objfile);
+ const solib *solib = find_one_solib_for_objfile (&objfile);
CORE_ADDR solib_base = find_debug_base_for_solib (solib);
if (solib_base == 0)
solib_base = default_debug_base;
diff --git a/gdb/solib.c b/gdb/solib.c
index 1211874d22be..b2cb3870c6e0 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -633,6 +633,7 @@ solib_read_symbols (solib &so, symfile_add_flags flags)
so.objfile->addr_low = so.addr_low;
}
+ so.objfile->add_solib (so);
so.symbols_loaded = true;
}
catch (const gdb_exception_error &e)
@@ -703,10 +704,14 @@ remove_solib (program_space *pspace,
notify_solib_unloaded (pspace, *solib_it, still_in_use, false);
/* Unless the user loaded it explicitly, free SO's objfile. */
- if (solib_it->objfile != nullptr
- && !(solib_it->objfile->flags & OBJF_USERLOADED)
- && !still_in_use)
- solib_it->objfile->unlink ();
+ if (solib_it->objfile != nullptr)
+ {
+ /* Remove the objfile -> solib backlink. */
+ solib_it->objfile->remove_solib (*solib_it);
+
+ if (!(solib_it->objfile->flags & OBJF_USERLOADED) && !still_in_use)
+ solib_it->objfile->unlink ();
+ }
pspace->deleted_solibs.push_back (solib_it->name);
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 09/10] gdb: change default objfile iteration order to start with current objfile
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (7 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 08/10] gdb: add objfile -> solib backlink Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 10/10] gdb: multiple solib_ops per program space Simon Marchi
9 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi, Tom Tromey
From: Simon Marchi <simon.marchi@polymtl.ca>
The current default objfile search strategy, defined both in
solib_ops::iterate_over_objfiles_in_search_order (when the solib_ops
hasn't overridden it) and in
program_space::iterate_over_objfiles_in_search_order (when there is no
solib_ops in the program space, for some reason), is to scan the objfile
list linearly.
program_space::iterate_over_objfiles_in_search_order:
for (auto &objfile : this->objfiles ())
if (cb (&objfile))
return;
solib_ops::iterate_over_objfiles_in_search_order:
for (objfile &objfile : m_pspace->objfiles ())
if (cb (&objfile))
return;
The following patch adds support for multiple concurrent solib_ops in a
program space. Everything that deals with an solib_ops needs to be
adapted to deal with multiple solib_ops, including
program_space::iterate_over_objfiles_in_search_order. What I came up
with for that method introduces a change in behavior where the current
objfile will always be searched first. Rather than hide the change in
behavior in that big patch, do it in this preparatory patch, in the hope
that any potential problems it causes can be more easily bisected and
analyzed.
Concretely, the change is to check the current objfile first, in the two
spots:
if (current_objfile != nullptr && cb (current_objfile))
return;
This happens to be what windows_solib_ops did, so remove that
specialization.
svr4_solib_ops already has its own iterate_over_objfiles_in_search_order
that conditionally checks the current objfile first, based on the
DT_SYMBOLIC dynamic tag. So it is not affected by this change.
The solib_ops potentially affected by this change are (we know that
rocm_solib_ops doesn't care):
- frv_solib_ops
- target_solib_ops
- darwin_solib_ops
- dsbt_solib_ops
- aix_solib_ops
I don't really know if searching objfiles in order (so, main objfile
first typically) is essential for any of these solib_ops.
Intuitively, searching the current objfile first makes sense, unless
there is some fancy symbol interposition happening, in which case that
solib_ops should probably implement a custom
iterate_over_objfiles_in_search_order.
Change-Id: I58195af6b53ca202e6061a5f36d5f74e1a0d5c17
Approved-By: Tom Tromey <tom@tromey.com>
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=17003
---
gdb/progspace.c | 5 ++++-
gdb/solib.c | 5 ++++-
gdb/windows-tdep.c | 40 ----------------------------------------
3 files changed, 8 insertions(+), 42 deletions(-)
diff --git a/gdb/progspace.c b/gdb/progspace.c
index 1407b058dfdf..7023ccddc546 100644
--- a/gdb/progspace.c
+++ b/gdb/progspace.c
@@ -128,8 +128,11 @@ program_space::iterate_over_objfiles_in_search_order
return m_solib_ops->iterate_over_objfiles_in_search_order
(cb, current_objfile);
+ if (current_objfile != nullptr && cb (current_objfile))
+ return;
+
for (auto &objfile : this->objfiles ())
- if (cb (&objfile))
+ if (&objfile != current_objfile && cb (&objfile))
return;
}
diff --git a/gdb/solib.c b/gdb/solib.c
index b2cb3870c6e0..d2ae598815a5 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -450,8 +450,11 @@ solib_ops::iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const
{
+ if (current_objfile != nullptr && cb (current_objfile))
+ return;
+
for (objfile &objfile : m_pspace->objfiles ())
- if (cb (&objfile))
+ if (&objfile != current_objfile && cb (&objfile))
return;
}
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index 261c38b35635..d65e6e76faeb 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -829,9 +829,6 @@ struct windows_solib_ops : target_solib_ops
using target_solib_ops::target_solib_ops;
void create_inferior_hook (int from_tty) override;
- void iterate_over_objfiles_in_search_order
- (iterate_over_objfiles_in_search_order_cb_ftype cb,
- objfile *current_objfile) const override;
};
/* Return a new solib_ops for Windows systems. */
@@ -890,43 +887,6 @@ windows_solib_ops::create_inferior_hook (int from_tty)
}
}
-/* Implement the "iterate_over_objfiles_in_search_order" gdbarch
- method. It searches all objfiles, starting with CURRENT_OBJFILE
- first (if not NULL).
-
- On Windows, the system behaves a little differently when two
- objfiles each define a global symbol using the same name, compared
- to other platforms such as GNU/Linux for instance. On GNU/Linux,
- all instances of the symbol effectively get merged into a single
- one, but on Windows, they remain distinct.
-
- As a result, it usually makes sense to start global symbol searches
- with the current objfile before expanding it to all other objfiles.
- This helps for instance when a user debugs some code in a DLL that
- refers to a global variable defined inside that DLL. When trying
- to print the value of that global variable, it would be unhelpful
- to print the value of another global variable defined with the same
- name, but in a different DLL. */
-
-void
-windows_solib_ops::iterate_over_objfiles_in_search_order
- (iterate_over_objfiles_in_search_order_cb_ftype cb,
- objfile *current_objfile) const
-{
- if (current_objfile)
- {
- if (cb (current_objfile))
- return;
- }
-
- for (objfile &objfile : m_pspace->objfiles ())
- if (&objfile != current_objfile)
- {
- if (cb (&objfile))
- return;
- }
-}
-
/* Implement the "auto_wide_charset" gdbarch method. */
static const char *
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH RESEND v2 10/10] gdb: multiple solib_ops per program space
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
` (8 preceding siblings ...)
2026-06-08 20:00 ` [PATCH RESEND v2 09/10] gdb: change default objfile iteration order to start with current objfile Simon Marchi
@ 2026-06-08 20:00 ` Simon Marchi
9 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-06-08 20:00 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi, Tom Tromey
From: Simon Marchi <simon.marchi@polymtl.ca>
This patch adds the possibility for a program space to have multiple
solib_ops. The motivation for this is to support ROCm (GPU) debugging
more cleanly.
Currently, when debugging a ROCm program, in order to be able to list
device code objects (the equivalent of shared libraries but for the
GPU), we install an instance of rocm_solib_ops as the program space's
sole solib_ops. But in order to still be able to list host shared
libraries, the rocm_solib_ops wraps the previously installed solib_ops
(currently always an svr4_solib_ops instance) and forwards method calls
to it.
By allowing program spaces to use multiple solib_ops, each solib_ops can
work side by side and rocm_solib_ops won't need to care about the host
solib_ops.
This change starts by making program_space::m_solib_ops a vector of
solib_ops_up instead of a single solib_ops_up, and it propagates from
there.
program_space::set_solib_ops becomes program_space::add_solib_ops. I
put in the restriction that there can be only one instance of a given
kind of solib_ops in the vector (it wouldn't make sense to have two
svr4_solib_ops instances in there, it would likely be a bug).
Remove program_space::unset_solib_ops, but add
program_space::remove_solib_ops (to remove a single solib_ops) and
program_space::clear_solib_ops (to remove all solib_ops). Removing an
solib_ops now automatically removes all the solibs produced by that
solib_ops, to avoid having solibs with stale solib_ops backlinks.
Add program_space::find_solib_ops, which can be used to find the
instance of a given kind of solib_ops.
Then, the code in solib.c gets adapted to deal with the fact that there
might be multiple solib_ops. The logic is done on a case by case basis:
- update_solib_list: where we call open_symbol_file_object, iterate
over all solib_ops until we find one that knows how to find and open
the main executable
- also update_solib_list: where we fetch the list of current SOs from
the inferior and compare it against the list of SOs know to GDB,
iterate over all solib_ops and fetch current SOs from all of them.
When comparing the SOs fetched from the solib_ops with the SOs
known to GDB, also compare the solib_ops before considering it a
match.
- print_solib_list_table: to determine whether to print the namespace
column, iterate over all solib_ops until we find one for which
supports_namespaces returns true and num_active_namespaces returns
greater than 0. If the namespace column is printed and some solibs
are from an solib_ops that doesn't support namespaces, skip the
column.
- info_linker_namespace_command: iterate over all solib_ops. For each
solib_ops, print all namespaces.
- solib_keep_data_in_core: iterate over all solib_ops until one says
that the given memory range should be kept.
- clear_solib: call clear_solib on all solib_ops
- solib_create_inferior_hook: call create_inferior_hook on all
solib_ops
- in_solib_dynsym_resolve_code: iterate over all solib_ops until one
says that the given PC falls into dynsym resolve code
- update_solib_breakpoints: call update_breakpoints on all solib_ops
- handle_solib_event: call handle_event on all solib_ops. The event
normally concern only one of those solib_ops, but at the moment we
have no way to tell which solib_ops that is. Perhaps, in the future,
we can link bp_shlib_event breakpoints to which solib_ops the event
is for, and call handle_event just for that solib_ops. In the mean
time, it is expected that for solib_ops not concerned by the event,
calling handle_event will have no effect.
- reload_shared_libraries: call clear_solib on all solib_ops
- solib_linker_namespace_count: sum the number of active linker
namespace over all solib_ops. I don't know if this makes sense, but
there is no known case today of two solib_ops supporting namespaces
working side-by-side, so it's a theoretical problem for now.
With multiple solib_ops, the concept of
iterate_over_objfiles_in_search_order becomes a bit more complex. The
implementation is mostly based on suppositions on my part. The idea
implemented in this patch is to give priority to the solib_ops
responsible for an objfile when searching for a symbol. If you're
stopped debugging some GPU code and look up a variable, and there is a
variable by that name both on the host and GPU side, I believe it is
more likely that you are interested in the GPU one.
Since we're going to ask multiple solib_ops to search successively,
change solib_ops::iterate_over_objfiles_in_search_order to return a
bool, to indicate if the search was successful or if we should continue.
Then, change solib_ops::iterate_over_objfiles_in_search_order (the
implementation used by all except svr4_solib_ops) to only search the
objfiles from that solib_ops as well as (optionally) the main objfile
(program_space::symfile_object_file). The idea regarding the main
objfile is that it falls under the "linking domain" of the host-side
solib_ops, even though it wasn't produced by that solib_ops.
svr4_solib_ops has its own implementation of
iterate_over_objfiles_in_search_order, which does check the main
objfile.
Checking the main objfile is predicated by the HANDE_MAIN_OBJFILE
solib_ops constructor parameter, for which rocm_solib_ops passes false.
Then in program_space::iterate_over_objfiles_in_search_order, the search
strategy becomes:
- if there is a current objfile:
- if that objfile was contributed by an solib_ops:
- call that solib_ops' iterate_over_objfiles_in_search_order
- else:
- search the current objfile by itself
If we didn't find anything interesting, we continue the search by asking
other solib_ops to search:
- for all solib_ops except the one searched before (if any):
- call that solib_ops' iterate_over_objfiles_in_search_order
Finally, "orphan" objfiles (not mapped to any solib) haven't been
searched (except possibly the current objfile), so:
- for all objfile without an solib_ops backlink, except the current one (if any):
- search that objfile
Note that if the current objfile is the main one
(program_space::symfile_object_file), then we won't enter the host
solib_ops::iterate_over_objfiles_in_search_order in the first part,
because that objfile is not associated to any solib, and therefore to
any solib_ops. Instead, we will enter it in the first iteration of the
second part. This relies on the fact that the host solib_ops will
always be first in the solib_ops list, with complementary solib_ops
following. This is always true today, given the order in which
solib_ops instances are pushed. Otherwise, we could go look for the
solib_ops for which M_HANDLE_MAIN_OBJFILE is true, but it didn't seem
like it was worth the extra complexity.
I wrote the gdb.rocm/symbol-lookup.exp test to try to exercise this
symbol lookup order thing. The test program loads some shared libraries
and some ROCm code objects, then does some symbol lookups while stopped
in different places.
Finally, the goal of all this, rocm_solib_ops gets changed to not wrap a
host solib_ops anymore! rocm_solib_ops previously had to use
lm_info_svr4, to avoid confusing svr4_solib_ops. It doesn't have to
anymore, so introduce a simpler lm_info_rocm that just holds an address.
rocm_solib_ops no longer needs to implement a bunch of methods just to
forward the call to the host solib_ops, so remove them. In the methods
that rocm_solib_ops actually needs to implement, it no longer needs to
call the host ops, which simplifies things.
Things also get simpler at the points where we push a new
rocm_solib_ops.
Change-Id: I260cdc0dcb9b11f33040059f300719dacac63d69
Approved-By: Tom Tromey <tom@tromey.com>
---
gdb/infcmd.c | 16 +-
gdb/inferior.h | 6 +-
gdb/infrun.c | 4 +-
gdb/progspace.c | 92 ++++++-
gdb/progspace.h | 57 ++--
gdb/solib-aix.c | 2 +-
gdb/solib-darwin.c | 2 +-
gdb/solib-dsbt.c | 2 +-
gdb/solib-frv.c | 2 +-
gdb/solib-rocm.c | 165 +++---------
gdb/solib-svr4.c | 32 ++-
gdb/solib-svr4.h | 6 +-
gdb/solib-target.c | 2 +-
gdb/solib.c | 268 +++++++++++--------
gdb/solib.h | 30 ++-
gdb/target.c | 2 +-
gdb/testsuite/gdb.rocm/symbol-lookup.cpp | 320 +++++++++++++++++++++++
gdb/testsuite/gdb.rocm/symbol-lookup.exp | 243 +++++++++++++++++
gdb/windows-tdep.c | 2 +-
19 files changed, 955 insertions(+), 298 deletions(-)
create mode 100644 gdb/testsuite/gdb.rocm/symbol-lookup.cpp
create mode 100644 gdb/testsuite/gdb.rocm/symbol-lookup.exp
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index bd5dbb02f485..8f7f5eecc669 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -366,7 +366,7 @@ strip_bg_char (const char *args, int *bg_char_p)
/* See inferior.h. */
void
-post_create_inferior (int from_tty, bool set_pspace_solib_ops)
+post_create_inferior (int from_tty, bool push_arch_solib_ops)
{
/* Be sure we own the terminal in case write operations are performed. */
target_terminal::ours_for_output ();
@@ -397,10 +397,16 @@ post_create_inferior (int from_tty, bool set_pspace_solib_ops)
throw;
}
- if (set_pspace_solib_ops)
- current_program_space->set_solib_ops
- (gdbarch_make_solib_ops (current_inferior ()->arch (),
- current_program_space));
+ if (push_arch_solib_ops)
+ {
+ /* This is called when an inferior gains execution. Any solib_ops
+ from previous executions should have been cleared by
+ target_pre_inferior. */
+ gdb_assert (current_program_space->solib_ops ().empty ());
+ current_program_space->add_solib_ops
+ (gdbarch_make_solib_ops (current_inferior ()->arch (),
+ current_program_space));
+ }
{
const unsigned solib_add_generation
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 9c031035a23d..19c5da76ca59 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -216,10 +216,10 @@ extern void setup_inferior (int from_tty);
means (running, attaching, connecting, et cetera). The target
should be stopped.
- If SET_PSPACE_SOLIB_OPS is true, initialize the program space's solib
- provider using the current inferior's architecture. */
+ If PUSH_ARCH_SOLIB_OPS is true, add an solib_ops to the current inferior's
+ program space provider using the current inferior's architecture. */
-extern void post_create_inferior (int from_tty, bool set_pspace_solib_ops);
+extern void post_create_inferior (int from_tty, bool push_arch_solib_ops);
extern void attach_command (const char *, int);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2edd6e0445b3..9dd4f7af3896 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1378,7 +1378,7 @@ follow_exec (ptid_t ptid, const char *exec_file_target)
we don't want those to be satisfied by the libraries of the
previous incarnation of this process. */
no_shared_libraries (current_program_space);
- current_program_space->unset_solib_ops ();
+ current_program_space->clear_solib_ops ();
inferior *execing_inferior = current_inferior ();
inferior *following_inferior;
@@ -1435,7 +1435,7 @@ follow_exec (ptid_t ptid, const char *exec_file_target)
registers. */
target_find_description ();
- following_inferior->pspace->set_solib_ops
+ following_inferior->pspace->add_solib_ops
(gdbarch_make_solib_ops (following_inferior->arch (),
following_inferior->pspace));
gdb::observers::inferior_execd.notify (execing_inferior, following_inferior);
diff --git a/gdb/progspace.c b/gdb/progspace.c
index 7023ccddc546..b513cfd6822a 100644
--- a/gdb/progspace.c
+++ b/gdb/progspace.c
@@ -98,6 +98,42 @@ program_space::~program_space ()
/* See progspace.h. */
+void
+program_space::clear_solib_ops ()
+{
+ while (!m_solib_ops.empty ())
+ this->remove_solib_ops (*m_solib_ops.front ());
+}
+
+/* See progspace.h. */
+
+void
+program_space::remove_solib_ops (const struct solib_ops &ops)
+{
+ auto ops_it
+ = std::find_if (m_solib_ops.begin (), m_solib_ops.end (),
+ [&ops] (const auto &up) { return up.get () == &ops; });
+
+ gdb_assert (ops_it != m_solib_ops.end ());
+
+ /* Remove all solibs provided by the solib_ops being removed. */
+ for (auto solibs_it = m_solib_list.begin ();
+ solibs_it != m_solib_list.end ();)
+ {
+ if (&solibs_it->ops () != &ops)
+ {
+ ++solibs_it;
+ continue;
+ }
+
+ solibs_it = remove_solib (this, solibs_it);
+ }
+
+ m_solib_ops.erase (ops_it);
+}
+
+/* See progspace.h. */
+
bool
program_space::multi_objfile_p () const
{
@@ -124,16 +160,58 @@ void
program_space::iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb, objfile *current_objfile)
{
- if (m_solib_ops != nullptr)
- return m_solib_ops->iterate_over_objfiles_in_search_order
- (cb, current_objfile);
+ struct solib_ops *curr_ops = nullptr;
- if (current_objfile != nullptr && cb (current_objfile))
- return;
+ if (current_objfile != nullptr)
+ {
+ if (!current_objfile->solibs ().empty ())
+ {
+ /* If that objfile was created by an solib_ops, give a chance to that
+ solib_ops to iterate the relevant objfiles first.
+ If the objfile is associated to multiple struct solib, assume that
+ they are all from the same solib_ops (so, get the ops from the
+ first solib). */
+ curr_ops = ¤t_objfile->solibs ().front ()->ops ();
+
+ if (curr_ops->iterate_over_objfiles_in_search_order (cb,
+ current_objfile))
+ return;
+ }
+ else
+ {
+ /* Otherwise, search this objfile first. */
+ if (cb (current_objfile))
+ return;
+ }
+ }
+
+ /* We didn't find what we were looking for based on the current objfile's
+ context, so expand the search. Ask the other solib_ops. */
+ for (auto &solib_ops : this->solib_ops ())
+ {
+ if (solib_ops.get () == curr_ops)
+ continue;
+
+ if (solib_ops->iterate_over_objfiles_in_search_order (cb, nullptr))
+ return;
+ }
+
+ /* Finally, look in the orphan objfiles. */
for (auto &objfile : this->objfiles ())
- if (&objfile != current_objfile && cb (&objfile))
- return;
+ {
+ /* The current objfile (if any) was already handled above. */
+ if (&objfile == current_objfile)
+ continue;
+
+ /* If the obfile has any associated solibs, then it is not orphan, it has
+ been handled above. */
+ if (!objfile.solibs ().empty ())
+ continue;
+
+ if (cb (&objfile))
+ return;
+ }
}
/* See progspace.h. */
diff --git a/gdb/progspace.h b/gdb/progspace.h
index 21977747cb94..a5ef3deb337d 100644
--- a/gdb/progspace.h
+++ b/gdb/progspace.h
@@ -242,26 +242,51 @@ struct program_space
is outside all objfiles in this progspace. */
struct objfile *objfile_for_address (CORE_ADDR address);
- /* Set this program space's solib provider.
+ /* Add OPS as an solib provider for this program space.
- The solib provider must be unset prior to calling this method. */
- void set_solib_ops (solib_ops_up ops)
+ There must not be another instance of SOLIB_OPS_TYPE in the solib_ops list
+ of this program space.
+
+ Return a non-owning reference to the ops. */
+ template <typename solib_ops_type>
+ solib_ops_type &add_solib_ops (std::unique_ptr<solib_ops_type> ops)
{
- gdb_assert (m_solib_ops == nullptr);
- m_solib_ops = std::move (ops);
+ gdb_assert (this->find_solib_ops<solib_ops_type> () == nullptr);
+
+ auto &ret = *ops;
+ m_solib_ops.emplace_back (std::move (ops));
+ return ret;
};
- /* Unset and free this program space's solib provider. */
- void unset_solib_ops ()
- { m_solib_ops = nullptr; }
+ /* Clear the list of solib providers for this program space.
- /* Unset and return this program space's solib provider. */
- solib_ops_up release_solib_ops ()
- { return std::move (m_solib_ops); }
+ Remove all solibs from the program space. */
+ void clear_solib_ops ();
- /* Get this program space's solib provider. */
- struct solib_ops *solib_ops () const
- { return m_solib_ops.get (); }
+ /* Remove OPS from the list of solib_ops of this program space.
+
+ OPS must be present in the solib_ops list.
+
+ Remove all solibs created by OPS from the program space. */
+ void remove_solib_ops (const solib_ops &ops);
+
+ /* Return a view of the solib providers for this program space. */
+ std::vector<solib_ops_up> &solib_ops ()
+ { return m_solib_ops; }
+
+ /* Find an solib_ops instance of type SOLIB_OPS_TYPE.
+
+ Return nullptr if not found. */
+ template <typename solib_ops_type>
+ solib_ops_type *find_solib_ops ()
+ {
+ for (auto &ops : this->solib_ops ())
+ if (auto ret = dynamic_cast<solib_ops_type *> (ops.get ());
+ ret != nullptr)
+ return ret;
+
+ return nullptr;
+ }
/* Return the list of all the solibs in this program space. */
owning_intrusive_list<solib> &solibs ()
@@ -401,8 +426,8 @@ struct program_space
/* All known objfiles are kept in a linked list. */
owning_intrusive_list<objfile> m_objfiles_list;
- /* solib_ops implementation used to provide solibs in this program space. */
- solib_ops_up m_solib_ops;
+ /* solib_ops implementations used to provide solibs in this program space. */
+ std::vector<solib_ops_up> m_solib_ops;
/* List of shared objects mapped into this space. Managed by
solib.c. */
diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c
index 28f69ed9cbb4..28bae833efbc 100644
--- a/gdb/solib-aix.c
+++ b/gdb/solib-aix.c
@@ -42,7 +42,7 @@ struct aix_solib_ops : public solib_ops
solib_ops_up
make_aix_solib_ops (program_space *pspace)
{
- return std::make_unique<aix_solib_ops> (pspace);
+ return std::make_unique<aix_solib_ops> (pspace, true);
}
/* Our private data in struct solib. */
diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c
index 07cd88d0e0c6..11ed0bb154e9 100644
--- a/gdb/solib-darwin.c
+++ b/gdb/solib-darwin.c
@@ -52,7 +52,7 @@ struct darwin_solib_ops : public solib_ops
solib_ops_up
make_darwin_solib_ops (program_space *pspace)
{
- return std::make_unique<darwin_solib_ops> (pspace);
+ return std::make_unique<darwin_solib_ops> (pspace, true);
}
struct gdb_dyld_image_info
diff --git a/gdb/solib-dsbt.c b/gdb/solib-dsbt.c
index 22b9a581546a..54cd4257d555 100644
--- a/gdb/solib-dsbt.c
+++ b/gdb/solib-dsbt.c
@@ -138,7 +138,7 @@ struct dsbt_solib_ops : public solib_ops
solib_ops_up
make_dsbt_solib_ops (program_space *pspace)
{
- return std::make_unique<dsbt_solib_ops> (pspace);
+ return std::make_unique<dsbt_solib_ops> (pspace, true);
}
/* Link map info to include in an allocated solib entry */
diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c
index cff3c3ca0193..7b444ecb3ea3 100644
--- a/gdb/solib-frv.c
+++ b/gdb/solib-frv.c
@@ -46,7 +46,7 @@ struct frv_solib_ops : public solib_ops
solib_ops_up
make_frv_solib_ops (program_space *pspace)
{
- return std::make_unique<frv_solib_ops> (pspace);
+ return std::make_unique<frv_solib_ops> (pspace, true);
}
/* FR-V pointers are four bytes wide. */
diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
index 0a284dfb0a56..961a9a03f832 100644
--- a/gdb/solib-rocm.c
+++ b/gdb/solib-rocm.c
@@ -28,7 +28,6 @@
#include "linux-tdep.h"
#include "observable.h"
#include "solib.h"
-#include "solib-svr4.h"
#include "symfile.h"
#include "filesystem.h"
@@ -171,33 +170,34 @@ rocm_solib_fd_cache::close (target_fd fd, fileio_error *target_errno)
struct rocm_so
{
- rocm_so (const char *name, std::string unique_name, lm_info_svr4_up lm_info)
- : name (name),
+ rocm_so (std::string name, std::string unique_name, CORE_ADDR load_addr)
+ : name (std::move (name)),
unique_name (std::move (unique_name)),
- lm_info (std::move (lm_info))
+ load_addr (load_addr)
{}
std::string name, unique_name;
- lm_info_svr4_up lm_info;
+ CORE_ADDR load_addr;
+};
+
+/* Private data for ROCm solibs. */
+
+struct lm_info_rocm : public lm_info
+{
+ lm_info_rocm (CORE_ADDR load_addr)
+ : load_addr (load_addr)
+ {}
+
+ CORE_ADDR load_addr;
};
/* 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 (inferior *inf, solib_ops_up host_ops)
- : solib_ops (inf->pspace), m_host_ops (std::move (host_ops)),
- m_inf (inf), m_fd_cache (inf)
- {
- gdb_assert (m_host_ops != nullptr);
- gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
- }
-
- /* Release the host solib_ops. */
- solib_ops_up release_host_ops ()
- { return std::move (m_host_ops); }
+ explicit rocm_solib_ops (inferior *inf)
+ : solib_ops (inf->pspace, false), m_inf (inf), m_fd_cache (inf)
+ { }
/* The methods implemented by rocm_solib_ops. */
owning_intrusive_list<solib> current_sos () override;
@@ -210,60 +210,11 @@ struct rocm_solib_ops : public solib_ops
rocm_solib_target_inferior_created. */
void update_solib_list ();
- /* 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) const override
- { return m_host_ops->clear_solib (pspace); }
-
- bool open_symbol_file_object (int from_tty) const 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) const 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); }
-
- void iterate_over_objfiles_in_search_order
- (iterate_over_objfiles_in_search_order_cb_ftype cb,
- objfile *current_objfile) const override
- {
- return m_host_ops->iterate_over_objfiles_in_search_order
- (cb, current_objfile);
- }
-
private:
owning_intrusive_list<solib>
solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd);
- solib_ops_up m_host_ops;
-
/* Inferior this rocm_solib_ops is for. */
inferior *m_inf;
@@ -280,28 +231,16 @@ void
rocm_solib_ops::relocate_section_addresses (solib &so,
struct target_section *sec) const
{
- if (!is_amdgpu_arch (so.abfd.get ()))
- {
- m_host_ops->relocate_section_addresses (so, sec);
- return;
- }
+ gdb_assert (is_amdgpu_arch (so.abfd.get ()));
- auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
- sec->addr = sec->addr + li->l_addr;
- sec->endaddr = sec->endaddr + li->l_addr;
+ auto &li = gdb::checked_static_cast<lm_info_rocm &> (*so.lm_info);
+ sec->addr = sec->addr + li.load_addr;
+ sec->endaddr = sec->endaddr + li.load_addr;
}
void
rocm_solib_ops::handle_event ()
{
- /* 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). */
- m_host_ops->handle_event ();
-
this->update_solib_list ();
}
@@ -313,7 +252,7 @@ rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
owning_intrusive_list<solib> dst;
for (const rocm_so &so : sos)
- dst.emplace_back (std::make_unique<lm_info_svr4> (*so.lm_info),
+ dst.emplace_back (std::make_unique<lm_info_rocm> (so.load_addr),
so.unique_name, so.name, *this);
return dst;
@@ -325,22 +264,7 @@ rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
owning_intrusive_list<solib>
rocm_solib_ops::current_sos ()
{
- /* First, retrieve the host-side shared library list. */
- owning_intrusive_list<solib> sos = m_host_ops->current_sos ();
-
- /* Then, the device-side shared library list. */
- if (m_solib_list.empty ())
- return sos;
-
- owning_intrusive_list<solib> dev_solibs = solibs_from_rocm_sos (m_solib_list);
-
- if (sos.empty ())
- return dev_solibs;
-
- /* Append our libraries to the end of the list. */
- sos.splice (std::move (dev_solibs));
-
- return sos;
+ return solibs_from_rocm_sos (m_solib_list);
}
namespace {
@@ -680,9 +604,7 @@ rocm_solib_ops::bfd_iovec_open (bfd *abfd)
gdb_bfd_ref_ptr
rocm_solib_ops::bfd_open (const char *pathname)
{
- /* Handle regular files with SVR4 open. */
- if (strstr (pathname, "://") == nullptr)
- return m_host_ops->bfd_open (pathname);
+ gdb_assert (strstr (pathname, "://") != nullptr);
auto open = [this] (bfd *nbfd)
{
@@ -769,7 +691,6 @@ void
rocm_solib_ops::create_inferior_hook (int from_tty)
{
m_solib_list.clear ();
- m_host_ops->create_inferior_hook (from_tty);
}
void
@@ -816,10 +737,6 @@ rocm_solib_ops::update_solib_list ()
gdb::unique_xmalloc_ptr<char> uri_bytes_holder (uri_bytes);
- /* Pass a dummy debug base. */
- lm_info_svr4_up li = std::make_unique<lm_info_svr4> (-1);
- li->l_addr = l_addr;
-
/* Generate a unique name so that code objects with the same URI but
different load addresses are seen by gdb core as different shared
objects. */
@@ -827,7 +744,7 @@ rocm_solib_ops::update_solib_list ()
= string_printf ("code_object_%ld", code_object_list[i].handle);
m_solib_list.emplace_back (uri_bytes, std::move (unique_name),
- std::move (li));
+ l_addr);
}
}
@@ -838,12 +755,10 @@ rocm_solib_target_inferior_created (inferior *inf)
if (inf->vfork_parent != nullptr)
return;
- auto prev_ops = inf->pspace->release_solib_ops ();
- auto rocm_ops = std::make_unique<rocm_solib_ops> (inf, std::move (prev_ops));
- inf->pspace->set_solib_ops (std::move (rocm_ops));
+ auto &rocm_ops
+ = inf->pspace->add_solib_ops (std::make_unique<rocm_solib_ops> (inf));
- gdb::checked_static_cast<rocm_solib_ops *>
- (inf->pspace->solib_ops ())->update_solib_list ();
+ rocm_ops.update_solib_list ();
/* Force GDB to reload the solibs. */
inf->pspace->clear_solib_cache ();
@@ -858,11 +773,8 @@ rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf)
if (get_amd_dbgapi_process_id (follow_inf) == AMD_DBGAPI_PROCESS_NONE)
return;
- auto pspace = follow_inf->pspace;
- auto prev_ops = pspace->release_solib_ops ();
- auto rocm_ops
- = std::make_unique<rocm_solib_ops> (follow_inf, std::move (prev_ops));
- pspace->set_solib_ops (std::move (rocm_ops));
+ follow_inf->pspace->add_solib_ops
+ (std::make_unique<rocm_solib_ops> (follow_inf));
}
static void
@@ -872,14 +784,13 @@ rocm_solib_target_inferior_forked (inferior *parent_inf, inferior *child_inf,
{
if (detach_on_fork && follow_child && fork_kind == TARGET_WAITKIND_FORKED)
{
- /* In this particular configuration, infrun's follow_fork_inferior
- function moves the parent pspace to the child directly. Remove the
- existing rocm_solib_ops from the child and restore the host solib_ops,
- to make it look like a brand new pspace. */
- auto rocm_ops_holder = child_inf->pspace->release_solib_ops ();
- auto rocm_ops
- = gdb::checked_static_cast<rocm_solib_ops *> (rocm_ops_holder.get ());
- child_inf->pspace->set_solib_ops (rocm_ops->release_host_ops ());
+ /* In this particular configuration, infrun's follow_fork_inferior moves
+ the parent pspace to the child directly. Remove the existing rocm_solib_ops
+ from the child and restore the host solib_ops, to make it look like a
+ brand new pspace. */
+ auto rocm_ops = child_inf->pspace->find_solib_ops<rocm_solib_ops> ();
+ gdb_assert (rocm_ops != nullptr);
+ child_inf->pspace->remove_solib_ops (*rocm_ops);
}
}
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 33fb6e3cd970..04000f6797ed 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -3639,7 +3639,7 @@ find_debug_base_for_solib (const solib *solib)
stay in the same namespace as that file. Otherwise, we only consider
the initial namespace. */
-void
+bool
svr4_solib_ops::iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const
@@ -3665,7 +3665,7 @@ svr4_solib_ops::iterate_over_objfiles_in_search_order
{
checked_current_objfile = true;
if (cb (current_objfile))
- return;
+ return true;
}
}
@@ -3686,13 +3686,25 @@ svr4_solib_ops::iterate_over_objfiles_in_search_order
if (checked_current_objfile && &objfile == current_objfile)
continue;
- /* Try to determine the namespace into which objfile was loaded.
-
- If we fail, e.g. for manually added symbol files or for the main
- executable, we assume that they were added to the initial
- namespace. */
const solib *solib = find_one_solib_for_objfile (&objfile);
- CORE_ADDR solib_base = find_debug_base_for_solib (solib);
+ CORE_ADDR solib_base = 0;
+
+ if (solib != nullptr)
+ {
+ /* Skip objfiles provided by other solib_ops. */
+ if (&solib->ops () != this)
+ continue;
+
+ solib_base = find_debug_base_for_solib (solib);
+ }
+ else if (&objfile != m_pspace->symfile_object_file)
+ {
+ /* Objfiles not associated to an solib_ops are handled in the
+ program_space method. The main objfile is an exception to this,
+ because it is part of the SVR4 domain. */
+ continue;
+ }
+
if (solib_base == 0)
solib_base = default_debug_base;
@@ -3701,8 +3713,10 @@ svr4_solib_ops::iterate_over_objfiles_in_search_order
continue;
if (cb (&objfile))
- return;
+ return true;
}
+
+ return false;
}
std::optional<CORE_ADDR>
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index d7f58a76224d..69b79d051d3f 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -94,7 +94,9 @@ enum probe_action
struct svr4_solib_ops : public solib_ops
{
- using solib_ops::solib_ops;
+ explicit svr4_solib_ops (program_space *pspace)
+ : solib_ops (pspace, true)
+ {}
void relocate_section_addresses (solib &so, target_section *) const override;
void clear_so (const solib &so) const override;
@@ -112,7 +114,7 @@ struct svr4_solib_ops : public solib_ops
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;
- void iterate_over_objfiles_in_search_order
+ bool iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const override;
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index 0df8c6048aef..28558a03f5ba 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -390,5 +390,5 @@ target_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
solib_ops_up
make_target_solib_ops (program_space *pspace)
{
- return std::make_unique<target_solib_ops> (pspace);
+ return std::make_unique<target_solib_ops> (pspace, true);
}
diff --git a/gdb/solib.c b/gdb/solib.c
index d2ae598815a5..3b35a075f45d 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -21,6 +21,7 @@
#include <fcntl.h>
#include "exceptions.h"
#include "extract-store-integer.h"
+#include "progspace.h"
#include "symtab.h"
#include "bfd.h"
#include "build-id.h"
@@ -445,17 +446,38 @@ solib_ops::bfd_open (const char *pathname)
/* See solib.h. */
-void
+bool
solib_ops::iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const
{
if (current_objfile != nullptr && cb (current_objfile))
- return;
+ return true;
for (objfile &objfile : m_pspace->objfiles ())
- if (&objfile != current_objfile && cb (&objfile))
- return;
+ {
+ /* The current objfile, if any, was handled above. */
+ if (current_objfile == &objfile)
+ continue;
+
+ /* Call CB on OBJFILE if either:
+
+ - OBJFILE was provided by this solib_ops, or
+ - this is the main objfile and M_HANDLE_MAIN_OBJFILE is true
+ */
+ bool is_from_this = (!objfile.solibs ().empty ()
+ && &objfile.solibs ().front ()->ops () == this);
+ bool is_main_handled_by_this = (&objfile == m_pspace->symfile_object_file
+ && m_handle_main_objfile);
+
+ if (!is_from_this && !is_main_handled_by_this)
+ continue;
+
+ if (cb (&objfile))
+ return true;
+ }
+
+ return false;
}
/* Given a pointer to one of the shared objects in our list of mapped
@@ -688,14 +710,9 @@ notify_solib_unloaded (program_space *pspace, const solib &so,
gdb::observers::solib_unloaded.notify (pspace, so, still_in_use, silent);
}
-/* Remove solib *SOLIB_IT from PSPACE.
+/* See solib.h. */
- Remove the corresponding objfiles and target sections.
-
- Return an iterator to the next solib in PSPACE's solib list, helping to
- continue iterating. */
-
-static owning_intrusive_list<solib>::iterator
+owning_intrusive_list<solib>::iterator
remove_solib (program_space *pspace,
owning_intrusive_list<solib>::iterator solib_it)
{
@@ -730,11 +747,6 @@ remove_solib (program_space *pspace,
void
update_solib_list (int from_tty)
{
- solib_ops *ops = current_program_space->solib_ops ();
-
- if (ops == nullptr)
- return;
-
/* We can reach here due to changing solib-search-path or the
sysroot, before having any inferior. */
if (target_has_execution () && inferior_ptid != null_ptid)
@@ -749,7 +761,9 @@ update_solib_list (int from_tty)
{
try
{
- ops->open_symbol_file_object (from_tty);
+ for (const auto &ops : current_program_space->solib_ops ())
+ if (ops->open_symbol_file_object (from_tty))
+ break;
}
catch (const gdb_exception_error &ex)
{
@@ -784,7 +798,10 @@ update_solib_list (int from_tty)
the time we're done walking GDB's list, the inferior's list
contains only the new shared objects, which we then add. */
- owning_intrusive_list<solib> inferior = ops->current_sos ();
+ owning_intrusive_list<solib> inferior;
+ for (auto &ops : current_program_space->solib_ops ())
+ inferior.splice (ops->current_sos ());
+
owning_intrusive_list<solib>::iterator gdb_iter
= current_program_space->solibs ().begin ();
while (gdb_iter != current_program_space->solibs ().end ())
@@ -794,8 +811,13 @@ 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 (*gdb_iter, *inferior_iter))
- break;
+ {
+ if (&gdb_iter->ops () != &inferior_iter->ops ())
+ continue;
+
+ if (gdb_iter->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
@@ -1012,25 +1034,28 @@ print_solib_list_table (std::vector<const solib *> solib_list,
gdbarch *gdbarch = current_inferior ()->arch ();
/* "0x", a little whitespace, and two hex digits per byte of pointers. */
int addr_width = 4 + (gdbarch_ptr_bit (gdbarch) / 4);
- const solib_ops *ops = current_program_space->solib_ops ();
struct ui_out *uiout = current_uiout;
bool so_missing_debug_info = false;
- if (ops == nullptr)
- return;
+ /* The conditions for this command to print solib namespaces:
- /* There are 3 conditions for this command to print solib namespaces,
- 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 != nullptr
- && ops->supports_namespaces ()
- && ops->num_active_namespaces () > 1);
+ - PRINT_NAMESPACE has to be true
+ - at least one solib_ops has to have more than one active namespace
- int num_cols = 4;
+ Fold these into the PRINT_NAMESPACE condition. */
if (print_namespace)
- num_cols++;
+ {
+ print_namespace = false;
+
+ for (auto &ops : current_program_space->solib_ops ())
+ if (ops->supports_namespaces () && ops->num_active_namespaces () > 1)
+ {
+ print_namespace = true;
+ break;
+ }
+ }
+
+ int num_cols = print_namespace ? 5 : 4;
{
ui_out_emit_table table_emitter (uiout, num_cols, solib_list.size (),
@@ -1068,7 +1093,11 @@ print_solib_list_table (std::vector<const solib *> solib_list,
{
try
{
- uiout->field_fmt ("namespace", "%d", ops->find_solib_ns (*so));
+ if (so->ops ().supports_namespaces ())
+ uiout->field_fmt ("namespace", "%d",
+ so->ops ().find_solib_ns (*so));
+ else
+ uiout->field_skip ("namespace");
}
catch (const gdb_exception_error &er)
{
@@ -1151,81 +1180,92 @@ info_sharedlibrary_command (const char *pattern, int from_tty)
static void
info_linker_namespace_command (const char *pattern, int from_tty)
{
- const solib_ops *ops = current_program_space->solib_ops ();
-
- /* This command only really makes sense for inferiors that support
- linker namespaces, so we can leave early. */
- if (ops == nullptr || !ops->supports_namespaces ())
- error (_("Current inferior does not support linker namespaces. "
- "Use \"info sharedlibrary\" instead."));
-
- struct ui_out *uiout = current_uiout;
- std::vector<std::pair<int, std::vector<const solib *>>> all_solibs_to_print;
-
pattern = skip_spaces (pattern);
- if (pattern == nullptr || pattern[0] == '\0')
- {
- uiout->message (_("There are %d linker namespaces loaded.\n"),
- ops->num_active_namespaces ());
+ /* Will be set to true if at least one solib_ops in the program space
+ supports namespaces. */
+ bool one_solib_ops_supports_namespaces = false;
- int printed = 0;
- for (int i = 0; printed < ops->num_active_namespaces (); i++)
+ for (auto &ops : current_program_space->solib_ops ())
+ {
+ if (!ops->supports_namespaces ())
+ continue;
+
+ one_solib_ops_supports_namespaces = true;
+
+ struct ui_out *uiout = current_uiout;
+ std::vector<std::pair<int, std::vector<const solib *>>>
+ all_solibs_to_print;
+
+ if (pattern == nullptr || pattern[0] == '\0')
{
- std::vector<const solib *> solibs_to_print
- = ops->get_solibs_in_ns (i);
- if (solibs_to_print.size () > 0)
+ /* The output here will be awkward if multiple solib_ops support
+ namespaces, but it's just theoretical for now, so don't bother
+ trying to do anything fancier. */
+ uiout->message (_("There are %d linker namespaces loaded.\n"),
+ ops->num_active_namespaces ());
+
+ int printed = 0;
+ for (int i = 0; printed < ops->num_active_namespaces (); i++)
{
- all_solibs_to_print.push_back (std::make_pair
- (i, solibs_to_print));
- printed++;
+ std::vector<const solib *> solibs_to_print
+ = ops->get_solibs_in_ns (i);
+ if (solibs_to_print.size () > 0)
+ {
+ all_solibs_to_print.push_back (std::make_pair
+ (i, solibs_to_print));
+ printed++;
+ }
}
}
- }
- else
- {
- int ns;
- /* Check if the pattern includes the optional [[ and ]] decorators.
- To match multiple occurrences, '+' needs to be escaped, and every
- escape sequence must be doubled to survive the compiler pass. */
- re_comp ("^\\[\\[[0-9]\\+\\]\\]$");
- if (re_exec (pattern))
- ns = strtol (pattern + 2, nullptr, 10);
else
{
- char *end = nullptr;
- ns = strtol (pattern, &end, 10);
- if (end[0] != '\0')
- error (_("Invalid linker namespace identifier: %s"), pattern);
+ int ns;
+ /* Check if the pattern includes the optional [[ and ]] decorators.
+ To match multiple occurrences, '+' needs to be escaped, and every
+ escape sequence must be doubled to survive the compiler pass. */
+ re_comp ("^\\[\\[[0-9]\\+\\]\\]$");
+ if (re_exec (pattern))
+ ns = strtol (pattern + 2, nullptr, 10);
+ else
+ {
+ char *end = nullptr;
+ ns = strtol (pattern, &end, 10);
+ if (end[0] != '\0')
+ error (_("Invalid linker namespace identifier: %s"), pattern);
+ }
+
+ all_solibs_to_print.push_back
+ (std::make_pair (ns, ops->get_solibs_in_ns (ns)));
}
- all_solibs_to_print.push_back
- (std::make_pair (ns, ops->get_solibs_in_ns (ns)));
- }
-
- for (const auto &[ns, solibs_to_print] : all_solibs_to_print)
- {
- uiout->message ("\n");
-
- if (solibs_to_print.size () == 0)
+ for (const auto &[ns, solibs_to_print] : all_solibs_to_print)
{
- uiout->message (_("Linker namespace %d is not active.\n"), ns);
- /* If we got here, a specific namespace was requested, so there
- will only be one vector. We can leave early. */
- break;
+ uiout->message ("\n");
+
+ if (solibs_to_print.size () == 0)
+ {
+ uiout->message (_("Linker namespace %d is not active.\n"), ns);
+ /* If we got here, a specific namespace was requested, so there
+ will only be one vector. We can leave early. */
+ break;
+ }
+
+ if (solibs_to_print.size () == 1)
+ uiout->message
+ (_("1 library loaded in linker namespace %d:\n"), ns);
+ else
+ uiout->message
+ (_("%zu libraries loaded in linker namespace %d:\n"),
+ solibs_to_print.size (), ns);
+
+ print_solib_list_table (solibs_to_print, false);
}
-
- if (solibs_to_print.size () == 1)
- uiout->message
- (_("1 library loaded in linker namespace %d:\n"), ns);
- else
- uiout->message
- (_("%zu libraries loaded in linker namespace %d:\n"),
- solibs_to_print.size (), ns);
-
-
- print_solib_list_table (solibs_to_print, false);
}
+
+ if (!one_solib_ops_supports_namespaces)
+ error (_("Current inferior does not support linker namespaces. "
+ "Use \"info sharedlibrary\" instead."));
}
/* See solib.h. */
@@ -1273,9 +1313,11 @@ solib_ops::same (const solib &a, const solib &b) const
bool
solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
{
- const solib_ops *ops = current_program_space->solib_ops ();
+ for (const auto &ops : current_program_space->solib_ops ())
+ if (ops->keep_data_in_core (vaddr, size))
+ return true;
- return ops != nullptr && ops->keep_data_in_core (vaddr, size);
+ return false;
}
/* See solib.h. */
@@ -1294,8 +1336,7 @@ clear_solib (program_space *pspace)
pspace->solibs ().clear ();
- if (const solib_ops *ops = pspace->solib_ops ();
- ops != nullptr)
+ for (const auto &ops : pspace->solib_ops ())
ops->clear_solib (pspace);
}
@@ -1307,8 +1348,7 @@ clear_solib (program_space *pspace)
void
solib_create_inferior_hook (int from_tty)
{
- if (solib_ops *ops = current_program_space->solib_ops ();
- ops != nullptr)
+ for (const auto &ops : current_program_space->solib_ops ())
ops->create_inferior_hook (from_tty);
}
@@ -1317,9 +1357,11 @@ solib_create_inferior_hook (int from_tty)
bool
in_solib_dynsym_resolve_code (CORE_ADDR pc)
{
- const solib_ops *ops = current_program_space->solib_ops ();
+ for (const auto &ops : current_program_space->solib_ops ())
+ if (ops->in_dynsym_resolve_code (pc))
+ return true;
- return ops != nullptr && ops->in_dynsym_resolve_code (pc);
+ return false;
}
/* Implements the "sharedlibrary" command. */
@@ -1361,9 +1403,7 @@ no_shared_libraries_command (const char *ignored, int from_tty)
void
update_solib_breakpoints (void)
{
- const solib_ops *ops = current_program_space->solib_ops ();
-
- if (ops != nullptr)
+ for (const auto &ops : current_program_space->solib_ops ())
ops->update_breakpoints ();
}
@@ -1372,8 +1412,7 @@ update_solib_breakpoints (void)
void
handle_solib_event (void)
{
- if (solib_ops *ops = current_program_space->solib_ops ();
- ops != nullptr)
+ for (const auto &ops : current_program_space->solib_ops ())
ops->handle_event ();
current_inferior ()->pspace->clear_solib_cache ();
@@ -1471,8 +1510,7 @@ reload_shared_libraries (const char *ignored, int from_tty,
{
/* Reset or free private data structures not associated with
solib entries. */
- if (const solib_ops *ops = current_program_space->solib_ops ();
- ops != nullptr)
+ for (const auto &ops : current_program_space->solib_ops ())
ops->clear_solib (current_program_space);
/* Remove any previous solib event breakpoint. This is usually
@@ -1804,11 +1842,13 @@ remove_user_added_objfile (struct objfile *objfile)
int
solib_linker_namespace_count (program_space *pspace)
{
- if (const auto ops = pspace->solib_ops (); ops != nullptr
- && ops->supports_namespaces ())
- return ops->num_active_namespaces ();
+ int num_active_namespaces = 0;
- return 0;
+ for (const auto &ops : pspace->solib_ops ())
+ if (ops->supports_namespaces ())
+ num_active_namespaces += ops->num_active_namespaces ();
+
+ return num_active_namespaces;
}
/* Implementation of the linker_namespace convenience variable.
diff --git a/gdb/solib.h b/gdb/solib.h
index 5b5049f53435..57af984a1ae3 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -145,8 +145,8 @@ using iterate_over_objfiles_in_search_order_cb_ftype
struct solib_ops
{
- explicit solib_ops (program_space *pspace)
- : m_pspace (pspace)
+ solib_ops (program_space *pspace, bool handle_main_objfile)
+ : m_pspace (pspace), m_handle_main_objfile (handle_main_objfile)
{}
virtual ~solib_ops () = default;
@@ -276,21 +276,29 @@ struct solib_ops
virtual std::vector<const solib *> get_solibs_in_ns (int ns) const
{ gdb_assert_not_reached ("namespaces not supported"); }
- /* Iterate over all objfiles of the program space in the order that makes the
- most sense for the architecture to make global symbol searches.
+ /* Iterate over all objfiles associated to this solib_ops in the order that
+ makes the most sense for the architecture to make global symbol searches.
+
+ If M_HANDLE_MAIN_OBJFILE is set, also iterate over the "main" objfile of
+ the program space (program_space::symfile_object_file).
CB is a callback function passed an objfile to be searched. The iteration
stops if this function returns true.
If not nullptr, CURRENT_OBJFILE corresponds to the objfile being inspected
- when the symbol search was requested. */
- virtual void iterate_over_objfiles_in_search_order
+ when the symbol search was requested. If not nullptr, it is guaranteed
+ that CURRENT_OBJFILE is associated to this solib_ops. */
+ virtual bool iterate_over_objfiles_in_search_order
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const;
protected:
/* The program space for which this solib_ops was created. */
program_space *m_pspace;
+
+ /* Whether this solib_ops should handle the main objfile
+ (pspace->symfile_object_file) in iterate_over_objfiles_in_search_order. */
+ bool m_handle_main_objfile;
};
/* A unique pointer to an solib_ops. */
@@ -355,6 +363,16 @@ extern bool in_solib_dynsym_resolve_code (CORE_ADDR);
extern void no_shared_libraries (program_space *pspace);
+/* Remove solib *SOLIB_IT from PSPACE.
+
+ Remove the corresponding objfiles and target sections.
+
+ Return an iterator to the next solib in PSPACE's solib list, helping to
+ continue iterating. */
+
+extern owning_intrusive_list<solib>::iterator remove_solib
+ (program_space *pspace, owning_intrusive_list<solib>::iterator solib_it);
+
/* Synchronize GDB's shared object list with inferior's.
Extract the list of currently loaded shared objects from the
diff --git a/gdb/target.c b/gdb/target.c
index 5d937f3ae858..e680943d5b27 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2482,7 +2482,7 @@ target_pre_inferior ()
if (!gdbarch_has_global_solist (current_inferior ()->arch ()))
{
no_shared_libraries (current_program_space);
- current_program_space->unset_solib_ops ();
+ current_program_space->clear_solib_ops ();
invalidate_target_mem_regions ();
diff --git a/gdb/testsuite/gdb.rocm/symbol-lookup.cpp b/gdb/testsuite/gdb.rocm/symbol-lookup.cpp
new file mode 100644
index 000000000000..7de2f6a34992
--- /dev/null
+++ b/gdb/testsuite/gdb.rocm/symbol-lookup.cpp
@@ -0,0 +1,320 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022-2025 Free Software Foundation, Inc.
+
+ 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/>. */
+
+#if defined(SHARED_LIB_1)
+
+extern "C"
+{
+
+ using shared_lib_1_t = int;
+
+ void func_host_shared_lib_1 (shared_lib_1_t)
+ {
+ }
+
+ void func_host_shared_lib (shared_lib_1_t)
+ {
+ }
+
+ void func_1 (shared_lib_1_t)
+ {
+ }
+
+ void func_main_shared_lib_1 (shared_lib_1_t)
+ {
+ }
+
+} /* extern "C" */
+
+#elif defined(SHARED_LIB_2)
+
+extern "C"
+{
+
+ using shared_lib_2_t = int;
+
+ void func_host_shared_lib_2 (shared_lib_2_t)
+ {
+ }
+
+ void func_host_shared_lib (shared_lib_2_t)
+ {
+ }
+
+ void func_2 (shared_lib_2_t)
+ {
+ }
+
+ void func_main_shared_lib_2 (shared_lib_2_t)
+ {
+ }
+
+} /* extern "C" */
+
+#elif defined(CODE_OBJECT_1)
+
+extern "C"
+{
+
+ using code_object_1_t = int;
+
+ __global__ void func_device_code_object_1 (code_object_1_t)
+ {
+ }
+
+ __global__ void func_device_code_object (code_object_1_t)
+ {
+ }
+
+ __global__ void func_1 (code_object_1_t)
+ {
+ }
+
+ __global__ void func_main_code_object_1 (code_object_1_t)
+ {
+ }
+
+} /* extern "C" */
+
+#elif defined(CODE_OBJECT_2)
+
+extern "C"
+{
+
+ using code_object_2_t = int;
+
+ __global__ void func_device_code_object_2 (code_object_2_t)
+ {
+ }
+
+ __global__ void func_device_code_object (code_object_2_t)
+ {
+ }
+
+ __global__ void func_2 (code_object_2_t)
+ {
+ }
+
+ __global__ void func_main_code_object_2 (code_object_2_t)
+ {
+ }
+
+} /* extern "C" */
+
+#else
+
+#include <dlfcn.h>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include "hip/hip_runtime.h"
+
+/* This `extern "C"` is necessary to make the test work, due to an oddity in how
+ GDB computes the demangled names. Without it, the compiler includes a
+ DW_AT_linkage_name in the DW_TAG_subprogram DIE, causing GDB to use that to
+ compute the symbol's demangled name. The symbols would then appear as
+ `func_main(int)` rather than `func_main(main_program_t)`, making it
+ impossible to differentiate them. With `extern "C"`, GDB has to construct
+ the demangled name from the DIE structure, resulting in the expected
+ `func_main(main_program_t)`. */
+
+extern "C"
+{
+
+ using main_program_t = int;
+
+ void func_main (main_program_t)
+ {
+ }
+
+ void func_main_shared_lib_1 (main_program_t)
+ {
+ }
+
+ void func_main_shared_lib_2 (main_program_t)
+ {
+ }
+
+ void func_main_code_object_1 (main_program_t)
+ {
+ }
+
+ void func_main_code_object_2 (main_program_t)
+ {
+ }
+
+} /* extern "C" */
+
+namespace
+{
+
+void
+throw_from_hip_error (const std::string &prefix, hipError_t error)
+{
+ throw std::runtime_error (prefix + ": " + hipGetErrorString (error));
+}
+
+void
+warn_from_hip_error (const std::string &prefix, hipError_t error)
+{
+ std::cerr << prefix + ": " + hipGetErrorString (error) << std::endl;
+}
+
+struct hip_module_unloader
+{
+ void operator() (hipModule_t module) const
+ {
+ hipError_t error = hipModuleUnload (module);
+
+ if (error != HIP_SUCCESS)
+ warn_from_hip_error ("Failed to unload HIP module", error);
+ }
+};
+
+using hip_module_up
+ = std::unique_ptr<std::remove_pointer_t<hipModule_t>, hip_module_unloader>;
+
+hip_module_up
+load_hip_module (const char *module_path)
+{
+ hipModule_t module;
+ hipError_t error = hipModuleLoad (&module, module_path);
+
+ if (error != HIP_SUCCESS)
+ throw_from_hip_error ((std::string ("Failed to load HIP module ")
+ + module_path),
+ error);
+
+ return hip_module_up (module);
+}
+
+void
+throw_from_dlerror (const std::string &prefix)
+{
+ throw std::runtime_error (prefix + ": " + dlerror ());
+}
+
+void
+warn_from_dlerror (const std::string &prefix)
+{
+ std::cerr << prefix + ": " + dlerror () << std::endl;
+}
+
+struct dlcloser
+{
+ void operator() (void *lib)
+ {
+ int ret = dlclose (lib);
+
+ if (ret != 0)
+ warn_from_dlerror ("Failed to dlclose");
+ }
+};
+
+using dl_up = std::unique_ptr<void, dlcloser>;
+
+dl_up
+load_shared_lib (const char *lib_path)
+{
+ void *lib = dlopen (lib_path, RTLD_NOW);
+
+ if (lib == nullptr)
+ throw_from_dlerror (std::string ("Failed to dlopen ") + lib_path);
+
+ return dl_up (lib);
+}
+
+using host_function_t = void (*) (int);
+
+host_function_t
+lookup_symbol (dl_up &dl, const char *sym_name)
+{
+ void *sym = dlsym (dl.get (), sym_name);
+
+ if (sym == nullptr)
+ throw_from_dlerror (std::string ("Failed to dlsym ") + sym_name);
+
+ return reinterpret_cast<host_function_t> (sym);
+}
+
+hipFunction_t
+lookup_symbol (hip_module_up &mod, const char *sym_name)
+{
+ hipFunction_t func;
+ hipError_t error = hipModuleGetFunction (&func, mod.get (), sym_name);
+
+ if (error != HIP_SUCCESS)
+ throw_from_hip_error (std::string ("Failed to look up kernel ") + sym_name,
+ error);
+
+ return func;
+}
+
+void
+launch_kernel (hipFunction_t func)
+{
+ int arg = 2;
+ void *args[1] = { &arg };
+
+ hipError_t error = hipModuleLaunchKernel (func, 1, 1, 1, 1, 1, 1, 0, nullptr,
+ args, nullptr);
+
+ if (error != HIP_SUCCESS)
+ throw_from_hip_error ("Failed to launch kernel", error);
+
+ error = hipDeviceSynchronize ();
+
+ if (error != HIP_SUCCESS)
+ throw_from_hip_error ("Failed to sync", error);
+}
+
+} /* namespace */
+
+int
+main (int argc, const char **argv)
+{
+ if (argc != 5)
+ {
+ fprintf (stderr, "Usage: %s <shlib1> <shlib2> <hip1> <hip1>\n", argv[0]);
+ return 1;
+ }
+
+ dl_up shared_lib_1 = load_shared_lib (argv[1]);
+ dl_up shared_lib_2 = load_shared_lib (argv[2]);
+ hip_module_up hip_module_1 = load_hip_module (argv[3]);
+ hip_module_up hip_module_2 = load_hip_module (argv[4]);
+
+ host_function_t shared_lib_1_sym
+ = lookup_symbol (shared_lib_1, "func_host_shared_lib_1");
+ host_function_t shared_lib_2_sym
+ = lookup_symbol (shared_lib_2, "func_host_shared_lib_2");
+
+ hipFunction_t code_object_1_kernel
+ = lookup_symbol (hip_module_1, "func_device_code_object_1");
+ hipFunction_t code_object_2_kernel
+ = lookup_symbol (hip_module_2, "func_device_code_object_2");
+
+ func_main (0);
+ shared_lib_1_sym (0);
+ shared_lib_2_sym (0);
+ launch_kernel (code_object_1_kernel);
+ launch_kernel (code_object_2_kernel);
+
+ return 0;
+}
+
+#endif
diff --git a/gdb/testsuite/gdb.rocm/symbol-lookup.exp b/gdb/testsuite/gdb.rocm/symbol-lookup.exp
new file mode 100644
index 000000000000..1eb658a3e4c2
--- /dev/null
+++ b/gdb/testsuite/gdb.rocm/symbol-lookup.exp
@@ -0,0 +1,243 @@
+# Copyright 2025 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test symbol lookup with multiple host shared libraries and ROCm code objects.
+#
+# This test verifies that GDB correctly resolves symbols based on where the
+# program is stopped. The following objfiles are in play:
+#
+# - the main program
+# - two host shared libraries
+# - two ROCm code objects
+#
+# The main program is separated from host shared libraries for the purpose of
+# this test because it is handled differently than shared libraries by the
+# iterate_over_objfiles_in_search_order methods.
+#
+# Each objfile defines functions with various levels of uniqueness:
+#
+# - func_main: unique to the main program
+# - func_host_shared_lib_N: unique to host shared library N
+# - func_device_code_object_N: unique to ROCm code object N
+# - func_host_shared_lib: defined in both host shared libraries
+# - func_device_code_object: defined in both ROCm code objects
+# - func_N: defined in host shared library N and ROCm code object N
+# - func_main_shared_lib_N: defined in the main program and host shared library N
+# - func_main_code_object_N: defined in the main program and ROCm code object N
+#
+# The functions have different signatures, to be able to tell them apart.
+#
+# The test stops at breakpoints in each objfile and verifies that symbol
+# lookups return the expected function. GDB gives priority to the current
+# objfile, and then objfiles from the current "solib provider", before
+# considering other loaded objfiles.
+
+load_lib rocm.exp
+
+standard_testfile .cpp
+
+require allow_hipcc_tests
+
+# Build the main program.
+if { [build_executable "failed to prepare" \
+ $testfile $srcfile {debug hip}] == -1 } {
+ return
+}
+
+# Return the path to shared library N.
+proc shared_lib_path { n } {
+ return [standard_output_file ${::testfile}_${n}.so]
+}
+
+# Return the path to code object N.
+proc code_object_path { n } {
+ return [standard_output_file ${::testfile}_${n}.co]
+}
+
+# Build the host shared libraries and ROCm code objects.
+foreach n {1 2} {
+ set shared_lib [shared_lib_path $n]
+ set code_object [code_object_path $n]
+
+ # Build the host shlib N
+ if { [gdb_compile_shlib $srcdir/$subdir/$srcfile $shared_lib \
+ [list debug additional_flags=-DSHARED_LIB_$n]] != "" } {
+ return
+ }
+
+ # Build the ROCm code object N
+ if { [gdb_compile $srcdir/$subdir/$srcfile $code_object object \
+ [list debug hip additional_flags=--genco additional_flags=-DCODE_OBJECT_$n]] != "" } {
+ return
+ }
+}
+
+# Verify symbol lookup results at the current stop location.
+#
+# Each parameter is a regex matching the expected parameter type in the
+# function signature. This parameter type is what allows us to confirm
+# that GDB found the expected version of the function.
+proc check_funcs { func_host_shared_lib func_device_code_object
+ func_1 func_2
+ func_main_shared_lib_1 func_main_shared_lib_2
+ func_main_code_object_1 func_main_code_object_2 } {
+ # Unique functions: only one definition exists, so lookup is unambiguous.
+ gdb_test "print func_main" "<func_main\\(main_program_t\\)>"
+ gdb_test "print func_host_shared_lib_1" "<func_host_shared_lib_1\\(shared_lib_1_t\\)>"
+ gdb_test "print func_host_shared_lib_2" "<func_host_shared_lib_2\\(shared_lib_2_t\\)>"
+ gdb_test "print func_device_code_object_1" "<func_device_code_object_1\\(code_object_1_t\\)>"
+ gdb_test "print func_device_code_object_2" "<func_device_code_object_2\\(code_object_2_t\\)>"
+
+ # Functions defined in either both host shlibs or both code objects.
+ gdb_test "print func_host_shared_lib" "<func_host_shared_lib\\($func_host_shared_lib\\)>"
+ gdb_test "print func_device_code_object" "<func_device_code_object\\($func_device_code_object\\)>"
+
+ # Functions defined in one host shlib and one code object.
+ gdb_test "print func_1" "<func_1\\($func_1\\)>"
+ gdb_test "print func_2" "<func_2\\($func_2\\)>"
+
+ # Functions defined in main program and one objfile.
+ gdb_test "print func_main_shared_lib_1" "<func_main_shared_lib_1\\($func_main_shared_lib_1\\)>"
+ gdb_test "print func_main_shared_lib_2" "<func_main_shared_lib_2\\($func_main_shared_lib_2\\)>"
+ gdb_test "print func_main_code_object_1" "<func_main_code_object_1\\($func_main_code_object_1\\)>"
+ gdb_test "print func_main_code_object_2" "<func_main_code_object_2\\($func_main_code_object_2\\)>"
+}
+
+proc do_test {} {
+ clean_restart
+ gdb_load $::binfile
+
+ set shared_lib_1 [shared_lib_path 1]
+ set shared_lib_2 [shared_lib_path 2]
+ set code_object_1 [code_object_path 1]
+ set code_object_2 [code_object_path 2]
+
+ gdb_test_no_output "set args $shared_lib_1 $shared_lib_2 $code_object_1 $code_object_2" \
+ "set args"
+
+ with_rocm_gpu_lock {
+ if ![runto_main] {
+ return
+ }
+
+ # Set a breakpoint in the main program after all libraries / code
+ # objects have been loaded, as well as one breakpoint in each objfile.
+ gdb_test "break func_main" \
+ "Breakpoint $::decimal at $::hex.*"
+ gdb_test "with breakpoint pending on -- break func_host_shared_lib_1" \
+ "Breakpoint $::decimal \\(func_host_shared_lib_1\\) pending."
+ gdb_test "with breakpoint pending on -- break func_host_shared_lib_2" \
+ "Breakpoint $::decimal \\(func_host_shared_lib_2\\) pending."
+ gdb_test "with breakpoint pending on -- break func_device_code_object_1" \
+ "Breakpoint $::decimal \\(func_device_code_object_1\\) pending."
+ gdb_test "with breakpoint pending on -- break func_device_code_object_2" \
+ "Breakpoint $::decimal \\(func_device_code_object_2\\) pending."
+
+ # Stop in main program. We expect:
+ # - func_host_shared_lib to resolve to either shared lib (no preference)
+ # - func_device_code_object to resolve to either code object (no preference)
+ # - func_1 to resolve to shared_lib_1 (current solib provider)
+ # - func_2 to resolve to shared_lib_2 (current solib provider)
+ # - func_main_* symbols to resolve to main program's version (current objfile)
+ with_test_prefix "main program" {
+ gdb_test "continue" "hit Breakpoint $::decimal, func_main .*"
+ check_funcs \
+ "shared_lib_._t" \
+ "code_object_._t" \
+ "shared_lib_1_t" \
+ "shared_lib_2_t" \
+ "main_program_t" \
+ "main_program_t" \
+ "main_program_t" \
+ "main_program_t"
+ }
+
+ # Stop in host shared library 1. We expect:
+ # - func_host_shared_lib to resolve to shared_lib_1's version (current objfile)
+ # - func_device_code_object to resolve to either code object (no preference)
+ # - func_1 to resolve to shared_lib_1's version (current objfile)
+ # - func_2 to resolve to shared_lib_2's version (current solib provider)
+ # - func_main_shared_lib_1 to resolve to shared_lib_1's version (current objfile)
+ # - func_main_shared_lib_2 to resolve to either main or shared_lib_2 (no preference)
+ # - func_main_code_object_1 to resolve to either main or code_object_1 (no preference)
+ # - func_main_code_object_2 to resolve to either main or code_object_2 (no preference)
+ with_test_prefix "host shared lib 1" {
+ gdb_test "continue" "hit Breakpoint $::decimal, func_host_shared_lib_1 .*"
+ check_funcs \
+ "shared_lib_1_t" \
+ "code_object_._t" \
+ "shared_lib_1_t" \
+ "shared_lib_2_t" \
+ "shared_lib_1_t" \
+ "(main_program_t|shared_lib_2_t)" \
+ "(main_program_t|code_object_1_t)" \
+ "(main_program_t|code_object_2_t)"
+ }
+
+ # Stop in host shared library 2. Similar logic but now shared_lib_2
+ # is the current objfile.
+ with_test_prefix "host shared lib 2" {
+ gdb_test "continue" "hit Breakpoint $::decimal, func_host_shared_lib_2 .*"
+ check_funcs \
+ "shared_lib_2_t" \
+ "code_object_._t" \
+ "shared_lib_1_t" \
+ "shared_lib_2_t" \
+ "(main_program_t|shared_lib_1_t)" \
+ "shared_lib_2_t" \
+ "(main_program_t|code_object_1_t)" \
+ "(main_program_t|code_object_2_t)"
+ }
+
+ # Stop in ROCm code object 1. We expect:
+ # - func_host_shared_lib to resolve to either shared lib (no preference)
+ # - func_device_code_object to resolve to code_object_1's version (current objfile)
+ # - func_1 to resolve to code_object_1's version (current objfile)
+ # - func_2 to resolve to code_object_2's version (current solib provider)
+ # - func_main_shared_lib_1 to resolve to either main or shared_lib_1 (no preference)
+ # - func_main_shared_lib_2 to resolve to either main or shared_lib_2 (no preference)
+ # - func_main_code_object_1 to resolve to code_object_1's version (current objfile)
+ # - func_main_code_object_2 to resolve to either main or code_object_2 (no preference)
+ with_test_prefix "device code object 1" {
+ gdb_test "continue" "hit Breakpoint $::decimal, func_device_code_object_1 .*"
+ check_funcs \
+ "shared_lib_._t" \
+ "code_object_1_t" \
+ "code_object_1_t" \
+ "code_object_2_t" \
+ "(main_program_t|shared_lib_1_t)" \
+ "(main_program_t|shared_lib_2_t)" \
+ "code_object_1_t" \
+ "(main_program_t|code_object_2_t)"
+ }
+
+ # Stop in ROCm code object 2. Similar logic but now code_object_2
+ # is the current objfile.
+ with_test_prefix "device code object 2" {
+ gdb_test "continue" "hit Breakpoint $::decimal, func_device_code_object_2 .*"
+ check_funcs \
+ "shared_lib_._t" \
+ "code_object_2_t" \
+ "code_object_1_t" \
+ "code_object_2_t" \
+ "(main_program_t|shared_lib_1_t)" \
+ "(main_program_t|shared_lib_2_t)" \
+ "(main_program_t|code_object_1_t)" \
+ "code_object_2_t"
+ }
+ }
+}
+
+do_test
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index d65e6e76faeb..2201c17818be 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -836,7 +836,7 @@ struct windows_solib_ops : target_solib_ops
static solib_ops_up
make_windows_solib_ops (program_space *pspace)
{
- return std::make_unique<windows_solib_ops> (pspace);
+ return std::make_unique<windows_solib_ops> (pspace, true);
}
/* Implement the "solib_create_inferior_hook" solib_ops method. */
--
2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops
2026-06-08 20:00 ` [PATCH RESEND v2 01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops Simon Marchi
@ 2026-07-06 17:08 ` Lancelot SIX
0 siblings, 0 replies; 18+ messages in thread
From: Lancelot SIX @ 2026-07-06 17:08 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On Mon, Jun 08, 2026 at 04:00:25PM -0400, Simon Marchi wrote:
> In some fork cases, the rocm_solib_target_inferior_created observer gets
> called while the child inferior (passed as a parameter) already has a
> rocm_solib_ops installed. Since we unconditionally wrap the existing
> solib_ops with a new rocm_solib_ops, we can end up with a chain of
> multiple rocm_solib_ops, like:
>
> rocm_solib_ops -> rocm_solib_ops -> svr4_solib_ops
>
> I don't think it is technically harmful as of now (unless the process
> does a ton of forks and the rocm_solib_ops accumulate), but it is for
> sure useless. Add an assert for this in the rocm_solib_ops constructor,
> which reveals the cases where this happens.
>
> When a fork happens with follow-fork-mode == child and detach-on-fork
> on, infrun's follow_fork_inferior function directly moves the program
> space from the parent the child inferior, as an optimization. Coming
> into rocm_solib_target_inferior_created, the inferior's pspace
> unexpectedly already has a rocm_solib_ops pushed.
>
> Fix this locally by using an inferior_forked observer. In the scenario
> described above, remove the rocm_solib_ops and restore the host
> solib_ops as the program space's solib_ops, to make it look as if infrun
> didn't do this trick. This requires adding two parameters to the
> inferior_forked observer (detach_on_fork and follow_child).
>
> This should probably be done by infrun directly at some point. The
> logic being that it's fine to do an optimization, but it should look as
> if it didn't occur. If infrun created a brand new pspace for the child,
> there wouldn't be a rocm_solib_ops there already. But I prefer a local
> fix for now.
>
> Finally, there are also the vfork cases, where the child inferior shares
> the program space with its parent, and therefore the child's program
> space already has a rocm_solib_ops installed. Address this case by
> returning early (the `inf->vfork_parent != nullptr` check), because
> there is nothing we want to do for a vfork child anyway.
Hi Simon,
Sorry for the very delayed reply. Given our previous discussions on
list (on the V1), this LGTM, thanks.
Approved-by: Lancelot Six <lancelot.six@amd.com> (amdgpu)
This of course only covers the amdgpu bits.
Best,
Lancelot
>
> Change-Id: I2e76d111e96f1e01b6799b04da9cdd6f6e2984c9
> ---
> gdb/amd-dbgapi-target.c | 3 ++-
> gdb/infrun.c | 3 ++-
> gdb/observable.h | 8 ++++++--
> gdb/solib-rocm.c | 31 +++++++++++++++++++++++++++++++
> 4 files changed, 41 insertions(+), 4 deletions(-)
>
> diff --git a/gdb/amd-dbgapi-target.c b/gdb/amd-dbgapi-target.c
> index d44f03d0b80a..6b2314f7de79 100644
> --- a/gdb/amd-dbgapi-target.c
> +++ b/gdb/amd-dbgapi-target.c
> @@ -2335,7 +2335,8 @@ amd_dbgapi_inferior_execd (inferior *exec_inf, inferior *follow_inf)
>
> static void
> amd_dbgapi_inferior_forked (inferior *parent_inf, inferior *child_inf,
> - target_waitkind fork_kind)
> + target_waitkind fork_kind, bool detach_on_fork,
> + bool follow_child)
> {
> if (child_inf != nullptr)
> {
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 22a056084861..2edd6e0445b3 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -699,7 +699,8 @@ holding the child stopped. Try \"set %ps\" or \"%ps\".\n"),
> target_follow_fork (child_inf, child_ptid, fork_kind, follow_child,
> detach_fork);
>
> - gdb::observers::inferior_forked.notify (parent_inf, child_inf, fork_kind);
> + gdb::observers::inferior_forked.notify (parent_inf, child_inf, fork_kind,
> + detach_fork, follow_child);
>
> /* target_follow_fork must leave the parent as the current inferior. If we
> want to follow the child, we make it the current one below. */
> diff --git a/gdb/observable.h b/gdb/observable.h
> index 9f1c33ba7100..979e9d1c3ace 100644
> --- a/gdb/observable.h
> +++ b/gdb/observable.h
> @@ -92,9 +92,13 @@ extern observable<inferior */* exec_inf */, inferior */* follow_inf */>
> the child (because we follow only the child or we follow both), CHILD_INF
> is the child inferior. Otherwise, CHILD_INF is nullptr.
>
> - FORK_KIND is TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED. */
> + FORK_KIND is TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED.
> +
> + DETACH_ON_FORK and FOLLOW_CHILD represent the "detach-on-fork" and
> + "follow-fork-mode" settings. */
> extern observable<inferior */* parent_inf */, inferior */* child_inf */,
> - target_waitkind /* fork_kind */> inferior_forked;
> + target_waitkind /* fork_kind */, bool /* detach_on_fork */,
> + bool /* follow_child */> inferior_forked;
>
> /* The shared library specified by SOLIB has been loaded. Note that
> when gdb calls this observer, the library's symbols probably
> diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
> index 34cb2bb9e57c..dcaa0ee980a3 100644
> --- a/gdb/solib-rocm.c
> +++ b/gdb/solib-rocm.c
> @@ -165,8 +165,14 @@ struct rocm_solib_ops : public solib_ops
> explicit rocm_solib_ops (program_space *pspace, solib_ops_up host_ops)
> : solib_ops (pspace), m_host_ops (std::move (host_ops))
> {
> + gdb_assert (m_host_ops != nullptr);
> + gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
> }
>
> + /* Release the host solib_ops. */
> + solib_ops_up release_host_ops ()
> + { return std::move (m_host_ops); }
> +
> /* The methods implemented by rocm_solib_ops. */
> owning_intrusive_list<solib> current_sos () const override;
> void create_inferior_hook (int from_tty) const override;
> @@ -820,6 +826,10 @@ rocm_update_solib_list ()
> static void
> rocm_solib_target_inferior_created (inferior *inf)
> {
> + /* A vfork child shares its pspace with its parent, do not touch anything. */
> + if (inf->vfork_parent != nullptr)
> + return;
> +
> get_solib_info (inf)->solib_list.clear ();
>
> auto prev_ops = inf->pspace->release_solib_ops ();
> @@ -851,6 +861,24 @@ rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf)
> get_solib_info (exec_inf)->solib_list.clear ();
> }
>
> +static void
> +rocm_solib_target_inferior_forked (inferior *parent_inf, inferior *child_inf,
> + target_waitkind fork_kind,
> + bool detach_on_fork, bool follow_child)
> +{
> + if (detach_on_fork && follow_child && fork_kind == TARGET_WAITKIND_FORKED)
> + {
> + /* In this particular configuration, infrun's follow_fork_inferior
> + function moves the parent pspace to the child directly. Remove the
> + existing rocm_solib_ops from the child and restore the host solib_ops,
> + to make it look like a brand new pspace. */
> + auto rocm_ops_holder = child_inf->pspace->release_solib_ops ();
> + auto rocm_ops
> + = gdb::checked_static_cast<rocm_solib_ops *> (rocm_ops_holder.get ());
> + child_inf->pspace->set_solib_ops (rocm_ops->release_host_ops ());
> + }
> +}
> +
> INIT_GDB_FILE (rocm_solib)
> {
> /* The dependency on the amd-dbgapi exists because solib-rocm's
> @@ -864,4 +892,7 @@ INIT_GDB_FILE (rocm_solib)
> gdb::observers::inferior_execd.attach
> (rocm_solib_target_inferior_execd, "solib-rocm",
> { &get_amd_dbgapi_target_inferior_execd_observer_token () });
> +
> + gdb::observers::inferior_forked.attach
> + (rocm_solib_target_inferior_forked, "solib-rocm");
> }
> --
> 2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime
2026-06-08 20:00 ` [PATCH RESEND v2 03/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime Simon Marchi
@ 2026-07-06 17:08 ` Lancelot SIX
2026-07-07 20:48 ` Simon Marchi
0 siblings, 1 reply; 18+ messages in thread
From: Lancelot SIX @ 2026-07-06 17:08 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On Mon, Jun 08, 2026 at 04:00:27PM -0400, Simon Marchi wrote:
> Make the management of the cached fds automatic.
>
> Change rocm_solib_fd_cache::open to return a cached_target_fd, an RAII
> type that calls rocm_solib_fd_cache::close on destruction.
> cached_target_fd holds the actual fd and a reference to the cache it
> comes from.
>
> Change rocm_code_object_stream_file to hold a cached_target_fd and
> remove the explicit destructor.
>
> This is not essential, I just thought it would be nice.
Hi Simon,
This overall looks good. Just have one minor nits below. Other than that,
Approved-by: Lancelot Six <lancelot.six@amd.com> (amdgpu)
>
> Change-Id: I2529ac1caebbe6b7e6e60d228a403064f6f38d06
> ---
> gdb/solib-rocm.c | 90 +++++++++++++++++++++++++++++++-----------------
> 1 file changed, 59 insertions(+), 31 deletions(-)
>
> diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
> index f96f0823200e..60e64c494f88 100644
> --- a/gdb/solib-rocm.c
> +++ b/gdb/solib-rocm.c
> @@ -40,22 +40,63 @@ struct rocm_solib_fd_cache
> explicit rocm_solib_fd_cache (inferior *inf) : m_inferior (inf) {}
> DISABLE_COPY_AND_ASSIGN (rocm_solib_fd_cache);
>
> + /* The open method returns an object of this type.
> +
> + On destruction, automatically call close to indicate the caller no longer
> + uses the fd. */
> + struct cached_target_fd
> + {
> + cached_target_fd (target_fd fd, rocm_solib_fd_cache &cache)
> + : m_fd (fd),
> + m_cache (cache)
> + {
> + }
> +
> + cached_target_fd (cached_target_fd &&other)
> + : m_fd (other.m_fd),
> + m_cache (other.m_cache)
> + {
> + other.m_fd = target_fd::INVALID;
> + }
> +
> + ~cached_target_fd ()
> + {
> + if (m_fd == target_fd::INVALID)
> + return;
> +
> + fileio_error target_errno;
> + if (m_cache.close (m_fd, &target_errno) != 0)
> + warning (_("Failed to close solib: %s"),
> + strerror (fileio_error_to_host (target_errno)));
> + }
> +
> + DISABLE_COPY_AND_ASSIGN (cached_target_fd);
> +
> + target_fd fd () const { return m_fd; }
> +
> +private:
Indentation seems wrong here.
> + target_fd m_fd;
> + rocm_solib_fd_cache &m_cache;
> + };
> +
> /* Return a read-only file descriptor to FILENAME and increment the
> associated reference count.
>
> Open the file FILENAME if it is not already opened, reuse the existing file
> descriptor otherwise.
>
> - On error target_fd::INVALID is returned, and TARGET_ERRNO is set. */
> - target_fd open (const std::string &filename, fileio_error *target_errno);
> + On error, return a cached_target_fd with target_fd::INVALID and set
> + *TARGET_ERRNO. */
> + cached_target_fd open (const std::string &filename,
> + fileio_error *target_errno);
>
> +private:
> /* Decrement the reference count to FD and close FD if the reference count
> reaches 0.
>
> On success, return 0. On error, return -1 and set TARGET_ERRNO. */
> int close (target_fd fd, fileio_error *target_errno);
>
> -private:
> struct refcnt_fd
> {
> refcnt_fd (target_fd fd, int refcnt) : fd (fd), refcnt (refcnt) {}
> @@ -73,7 +114,7 @@ struct rocm_solib_fd_cache
> gdb::unordered_string_map<refcnt_fd> m_cache;
> };
>
> -target_fd
> +rocm_solib_fd_cache::cached_target_fd
> rocm_solib_fd_cache::open (const std::string &filename,
> fileio_error *target_errno)
> {
> @@ -88,7 +129,7 @@ rocm_solib_fd_cache::open (const std::string &filename,
> m_cache.emplace (std::piecewise_construct,
> std::forward_as_tuple (filename),
> std::forward_as_tuple (fd, 1));
> - return fd;
> + return cached_target_fd { fd, *this };;
You have 2 ";" here. One is too much.
> }
> else
> {
> @@ -96,7 +137,7 @@ rocm_solib_fd_cache::open (const std::string &filename,
> already opened FD. */
> it->second.refcnt++;
> gdb_assert (it->second.fd != target_fd::INVALID);
> - return it->second.fd;
> + return cached_target_fd { it->second.fd, *this };
> }
> }
>
> @@ -356,7 +397,7 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
> {
> DISABLE_COPY_AND_ASSIGN (rocm_code_object_stream_file);
>
> - rocm_code_object_stream_file (rocm_solib_fd_cache &fd_cache, target_fd fd,
> + rocm_code_object_stream_file (rocm_solib_fd_cache::cached_target_fd fd,
> ULONGEST offset, ULONGEST size);
>
> file_ptr read (bfd *abfd, void *buf, file_ptr size,
> @@ -364,14 +405,9 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
>
> LONGEST size () override;
>
> - ~rocm_code_object_stream_file () override;
> -
> protected:
> - /* The fd cache owning this code object stream. */
> - rocm_solib_fd_cache &m_fd_cache;
> -
> /* The target file descriptor for this stream. */
> - target_fd m_fd;
> + rocm_solib_fd_cache::cached_target_fd m_fd;
>
> /* The offset of the ELF file image in the target file. */
> ULONGEST m_offset;
> @@ -382,9 +418,8 @@ struct rocm_code_object_stream_file final : rocm_code_object_stream
> };
>
> rocm_code_object_stream_file::rocm_code_object_stream_file
> - (rocm_solib_fd_cache &fd_cache, target_fd fd, ULONGEST offset,
> - ULONGEST size)
> - : m_fd_cache (fd_cache), m_fd (fd), m_offset (offset), m_size (size)
> + (rocm_solib_fd_cache::cached_target_fd fd, ULONGEST offset, ULONGEST size)
> + : m_fd (std::move (fd)), m_offset (offset), m_size (size)
> {
> }
>
> @@ -399,9 +434,9 @@ rocm_code_object_stream_file::read (bfd *, void *buf, file_ptr size,
> QUIT;
>
> file_ptr bytes_read
> - = target_fileio_pread (m_fd, static_cast<gdb_byte *> (buf) + nbytes,
> - size, m_offset + offset + nbytes,
> - &target_errno);
> + = target_fileio_pread (m_fd.fd (),
> + static_cast<gdb_byte *> (buf) + nbytes, size,
> + m_offset + offset + nbytes, &target_errno);
>
> if (bytes_read == 0)
> break;
> @@ -427,7 +462,7 @@ rocm_code_object_stream_file::size ()
> {
> fileio_error target_errno;
> struct stat stat;
> - if (target_fileio_fstat (m_fd, &stat, &target_errno) < 0)
> + if (target_fileio_fstat (m_fd.fd (), &stat, &target_errno) < 0)
> {
> errno = fileio_error_to_host (target_errno);
> bfd_set_error (bfd_error_system_call);
> @@ -447,14 +482,6 @@ rocm_code_object_stream_file::size ()
> return m_size;
> }
>
> -rocm_code_object_stream_file::~rocm_code_object_stream_file ()
> -{
> - fileio_error target_errno;
> - if (m_fd_cache.close (m_fd, &target_errno) != 0)
> - warning (_("Failed to close solib: %s"),
> - strerror (fileio_error_to_host (target_errno)));
> -}
> -
> /* Interface to a code object which lives in the inferior's memory. */
>
> struct rocm_code_object_stream_memory final : public rocm_code_object_stream
> @@ -610,16 +637,17 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
>
> auto info = get_solib_info (inferior);
> fileio_error target_errno;
> - target_fd fd = info->fd_cache.open (decoded_path, &target_errno);
> + rocm_solib_fd_cache::cached_target_fd fd
> + = info->fd_cache.open (decoded_path, &target_errno);
>
> - if (fd == target_fd::INVALID)
> + if (fd.fd () == target_fd::INVALID)
> {
> errno = fileio_error_to_host (target_errno);
> bfd_set_error (bfd_error_system_call);
> return nullptr;
> }
>
> - return new rocm_code_object_stream_file (info->fd_cache, fd, offset,
> + return new rocm_code_object_stream_file (std::move (fd), offset,
> size);
> }
>
> --
> 2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 4/10] gdb: de-constify some methods of solib_ops
2026-06-08 20:00 ` [PATCH RESEND v2 04/10] gdb: de-constify some methods of solib_ops Simon Marchi
@ 2026-07-06 17:09 ` Lancelot SIX
0 siblings, 0 replies; 18+ messages in thread
From: Lancelot SIX @ 2026-07-06 17:09 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On Mon, Jun 08, 2026 at 04:00:28PM -0400, Simon Marchi wrote:
> The following commit moves the ROCm solib data from a registry to the
> rocm_solib_ops object itself. This requires making some methods
> non-const, and it then cascades to a bunch of things. This will have to
> be done one day or another, when (I suspect) we'll move more solib_ops
> data out of registries and into the solib_ops objects themselves. Do it
> in this separate patch to avoid polluting that patch.
>
> No behavior change expected.
>
FYI, the solib-rocm part look good to me.
Approved-by: Lancelot Six <lancelot.six@amd.com> (amdgpu)
Best,
Lancelot
> Change-Id: Icb5e921a82e235817f8817a3aa3c20b2c9dc959f
> ---
> gdb/progspace.h | 2 +-
> gdb/solib-aix.c | 12 ++++++------
> gdb/solib-darwin.c | 12 ++++++------
> gdb/solib-dsbt.c | 8 ++++----
> gdb/solib-frv.c | 8 ++++----
> gdb/solib-rocm.c | 20 ++++++++++----------
> gdb/solib-svr4.c | 14 +++++++-------
> gdb/solib-svr4.h | 14 +++++++-------
> gdb/solib-target.c | 2 +-
> gdb/solib-target.h | 2 +-
> gdb/solib.c | 8 ++++----
> gdb/solib.h | 14 +++++++-------
> gdb/windows-tdep.c | 4 ++--
> 13 files changed, 60 insertions(+), 60 deletions(-)
>
> diff --git a/gdb/progspace.h b/gdb/progspace.h
> index e9261ff8590d..21977747cb94 100644
> --- a/gdb/progspace.h
> +++ b/gdb/progspace.h
> @@ -260,7 +260,7 @@ struct program_space
> { return std::move (m_solib_ops); }
>
> /* Get this program space's solib provider. */
> - const struct solib_ops *solib_ops () const
> + struct solib_ops *solib_ops () const
> { return m_solib_ops.get (); }
>
> /* Return the list of all the solibs in this program space. */
> diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c
> index 705760651217..28f69ed9cbb4 100644
> --- a/gdb/solib-aix.c
> +++ b/gdb/solib-aix.c
> @@ -32,9 +32,9 @@ struct aix_solib_ops : public solib_ops
> using solib_ops::solib_ops;
>
> void relocate_section_addresses (solib &so, target_section *) const override;
> - void create_inferior_hook (int from_tty) const override;
> - owning_intrusive_list<solib> current_sos () const override;
> - gdb_bfd_ref_ptr bfd_open (const char *pathname) 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. */
> @@ -425,7 +425,7 @@ solib_aix_get_section_offsets (struct objfile *objfile,
> }
>
> void
> -aix_solib_ops::create_inferior_hook (int from_tty) const
> +aix_solib_ops::create_inferior_hook (int from_tty)
> {
> const char *warning_msg = "unable to relocate main executable";
>
> @@ -454,7 +454,7 @@ aix_solib_ops::create_inferior_hook (int from_tty) const
> }
>
> owning_intrusive_list<solib>
> -aix_solib_ops::current_sos () const
> +aix_solib_ops::current_sos ()
> {
> std::optional<std::vector<lm_info_aix>> &library_list
> = solib_aix_get_library_list (current_inferior (), NULL);
> @@ -498,7 +498,7 @@ aix_solib_ops::current_sos () const
> }
>
> gdb_bfd_ref_ptr
> -aix_solib_ops::bfd_open (const char *pathname) const
> +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).
> diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c
> index 6e2535ca427f..07cd88d0e0c6 100644
> --- a/gdb/solib-darwin.c
> +++ b/gdb/solib-darwin.c
> @@ -42,9 +42,9 @@ struct darwin_solib_ops : public solib_ops
>
> void relocate_section_addresses (solib &so, target_section *) const override;
> void clear_solib (program_space *pspace) const override;
> - void create_inferior_hook (int from_tty) const override;
> - owning_intrusive_list<solib> current_sos () const override;
> - gdb_bfd_ref_ptr bfd_open (const char *pathname) 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-darwin.h. */
> @@ -211,7 +211,7 @@ find_program_interpreter (void)
> }
>
> owning_intrusive_list<solib>
> -darwin_solib_ops::current_sos () const
> +darwin_solib_ops::current_sos ()
> {
> type *ptr_type
> = builtin_type (current_inferior ()->arch ())->builtin_data_ptr;
> @@ -471,7 +471,7 @@ darwin_solib_read_all_image_info_addr (struct darwin_info *info)
> }
>
> void
> -darwin_solib_ops::create_inferior_hook (int from_tty) const
> +darwin_solib_ops::create_inferior_hook (int from_tty)
> {
> /* Everything below only makes sense if we have a running inferior. */
> if (!target_has_execution ())
> @@ -606,7 +606,7 @@ darwin_solib_ops::relocate_section_addresses (solib &so,
> }
>
> gdb_bfd_ref_ptr
> -darwin_solib_ops::bfd_open (const char *pathname) const
> +darwin_solib_ops::bfd_open (const char *pathname)
> {
> int found_file;
>
> diff --git a/gdb/solib-dsbt.c b/gdb/solib-dsbt.c
> index 052b170fd9a6..22b9a581546a 100644
> --- a/gdb/solib-dsbt.c
> +++ b/gdb/solib-dsbt.c
> @@ -128,8 +128,8 @@ struct dsbt_solib_ops : public solib_ops
>
> void relocate_section_addresses (solib &so, target_section *) const override;
> void clear_solib (program_space *pspace) const override;
> - void create_inferior_hook (int from_tty) const override;
> - owning_intrusive_list<solib> current_sos () const 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;
> };
>
> @@ -526,7 +526,7 @@ lm_base (void)
> we provide values for. */
>
> owning_intrusive_list<solib>
> -dsbt_solib_ops::current_sos () const
> +dsbt_solib_ops::current_sos ()
> {
> bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ());
> CORE_ADDR lm_addr;
> @@ -860,7 +860,7 @@ dsbt_relocate_main_executable (void)
> The shared library breakpoints also need to be enabled. */
>
> void
> -dsbt_solib_ops::create_inferior_hook (int from_tty) const
> +dsbt_solib_ops::create_inferior_hook (int from_tty)
> {
> /* Relocate main executable. */
> dsbt_relocate_main_executable ();
> diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c
> index 4f0aac31e733..cff3c3ca0193 100644
> --- a/gdb/solib-frv.c
> +++ b/gdb/solib-frv.c
> @@ -36,8 +36,8 @@ struct frv_solib_ops : public solib_ops
>
> void relocate_section_addresses (solib &so, target_section *) const override;
> void clear_solib (program_space *pspace) const override;
> - void create_inferior_hook (int from_tty) const override;
> - owning_intrusive_list<solib> current_sos () const 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;
> };
>
> @@ -321,7 +321,7 @@ lm_base (void)
> }
>
> owning_intrusive_list<solib>
> -frv_solib_ops::current_sos () const
> +frv_solib_ops::current_sos ()
> {
> bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ());
> CORE_ADDR lm_addr, mgot;
> @@ -789,7 +789,7 @@ frv_relocate_main_executable (void)
> enabled. */
>
> void
> -frv_solib_ops::create_inferior_hook (int from_tty) const
> +frv_solib_ops::create_inferior_hook (int from_tty)
> {
> /* Relocate main executable. */
> frv_relocate_main_executable ();
> diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
> index 60e64c494f88..4032ada28a0f 100644
> --- a/gdb/solib-rocm.c
> +++ b/gdb/solib-rocm.c
> @@ -215,11 +215,11 @@ struct rocm_solib_ops : public solib_ops
> { return std::move (m_host_ops); }
>
> /* The methods implemented by rocm_solib_ops. */
> - owning_intrusive_list<solib> current_sos () const override;
> - void create_inferior_hook (int from_tty) const override;
> - gdb_bfd_ref_ptr bfd_open (const char *pathname) const override;
> + 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 () 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
> @@ -270,7 +270,7 @@ struct rocm_solib_ops : public solib_ops
>
> private:
> owning_intrusive_list<solib>
> - solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const;
> + solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
>
> solib_ops_up m_host_ops;
> };
> @@ -303,7 +303,7 @@ rocm_solib_ops::relocate_section_addresses (solib &so,
> static void rocm_update_solib_list ();
>
> void
> -rocm_solib_ops::handle_event () const
> +rocm_solib_ops::handle_event ()
> {
> /* 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
> @@ -319,7 +319,7 @@ rocm_solib_ops::handle_event () const
> /* Create solib objects from rocm_so objects in SOS. */
>
> owning_intrusive_list<solib>
> -rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const
> +rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos)
> {
> owning_intrusive_list<solib> dst;
>
> @@ -334,7 +334,7 @@ rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const
> objects currently loaded in the inferior. */
>
> owning_intrusive_list<solib>
> -rocm_solib_ops::current_sos () const
> +rocm_solib_ops::current_sos ()
> {
> /* First, retrieve the host-side shared library list. */
> owning_intrusive_list<solib> sos = m_host_ops->current_sos ();
> @@ -692,7 +692,7 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
> }
>
> gdb_bfd_ref_ptr
> -rocm_solib_ops::bfd_open (const char *pathname) const
> +rocm_solib_ops::bfd_open (const char *pathname)
> {
> /* Handle regular files with SVR4 open. */
> if (strstr (pathname, "://") == nullptr)
> @@ -780,7 +780,7 @@ rocm_solib_ops::bfd_open (const char *pathname) const
> }
>
> void
> -rocm_solib_ops::create_inferior_hook (int from_tty) const
> +rocm_solib_ops::create_inferior_hook (int from_tty)
> {
> get_solib_info (current_inferior ())->solib_list.clear ();
>
> diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
> index 8e3de4d3ea1a..35c81000b10a 100644
> --- a/gdb/solib-svr4.c
> +++ b/gdb/solib-svr4.c
> @@ -1059,7 +1059,7 @@ svr4_solib_ops::clear_so (const solib &so) const
> /* Create the solib objects equivalent to the svr4_sos in SOS. */
>
> owning_intrusive_list<solib>
> -svr4_solib_ops::solibs_from_svr4_sos (const std::vector<svr4_so> &sos) const
> +svr4_solib_ops::solibs_from_svr4_sos (const std::vector<svr4_so> &sos)
> {
> owning_intrusive_list<solib> dst;
>
> @@ -1253,7 +1253,7 @@ svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list,
> linker, build a fallback list from other sources. */
>
> owning_intrusive_list<solib>
> -svr4_solib_ops::default_sos (svr4_info *info) const
> +svr4_solib_ops::default_sos (svr4_info *info)
> {
> if (!info->debug_loader_offset_p)
> return {};
> @@ -1452,7 +1452,7 @@ svr4_solib_ops::current_sos_direct (svr4_info *info) const
> /* Collect sos read and stored by the probes interface. */
>
> owning_intrusive_list<solib>
> -svr4_solib_ops::collect_probes_sos (svr4_info *info) const
> +svr4_solib_ops::collect_probes_sos (svr4_info *info)
> {
> owning_intrusive_list<solib> res;
>
> @@ -1469,7 +1469,7 @@ svr4_solib_ops::collect_probes_sos (svr4_info *info) const
> method. */
>
> owning_intrusive_list<solib>
> -svr4_solib_ops::current_sos_1 (svr4_info *info) const
> +svr4_solib_ops::current_sos_1 (svr4_info *info)
> {
> owning_intrusive_list<solib> sos;
>
> @@ -1496,7 +1496,7 @@ svr4_solib_ops::current_sos_1 (svr4_info *info) const
> /* Implement the "current_sos" solib_ops method. */
>
> owning_intrusive_list<solib>
> -svr4_solib_ops::current_sos () const
> +svr4_solib_ops::current_sos ()
> {
> svr4_info *info = get_svr4_info (current_program_space);
>
> @@ -2143,7 +2143,7 @@ svr4_solib_ops::disable_probes_interface (svr4_info *info) const
> standard interface. */
>
> void
> -svr4_solib_ops::handle_event () const
> +svr4_solib_ops::handle_event ()
> {
> struct svr4_info *info = get_svr4_info (current_program_space);
> struct probe_and_action *pa;
> @@ -3318,7 +3318,7 @@ svr4_relocate_main_executable (void)
> their symbols to be read at a later time. */
>
> void
> -svr4_solib_ops::create_inferior_hook (int from_tty) const
> +svr4_solib_ops::create_inferior_hook (int from_tty)
> {
> struct svr4_info *info;
>
> diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
> index 3078a092778c..d7f58a76224d 100644
> --- a/gdb/solib-svr4.h
> +++ b/gdb/solib-svr4.h
> @@ -99,14 +99,14 @@ 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) const override;
> - void create_inferior_hook (int from_tty) const override;
> - owning_intrusive_list<solib> current_sos () const override;
> + void create_inferior_hook (int from_tty) override;
> + owning_intrusive_list<solib> current_sos () override;
> bool open_symbol_file_object (int from_tty) const 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) const override;
> void update_breakpoints () const override;
> - void handle_event () 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;
> @@ -135,7 +135,7 @@ struct svr4_solib_ops : public solib_ops
> 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) const;
> + owning_intrusive_list<solib> default_sos (svr4_info *info);
> int read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
> CORE_ADDR debug_base, std::vector<svr4_so> &sos,
> int ignore_first) const;
> @@ -144,10 +144,10 @@ struct svr4_solib_ops : public solib_ops
> 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;
> - owning_intrusive_list<solib> collect_probes_sos (svr4_info *info) const;
> - owning_intrusive_list<solib> current_sos_1 (svr4_info *info) 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) const;
> + (const std::vector<svr4_so> &sos);
> void disable_probes_interface (svr4_info *info) const;
> void update_full (svr4_info *info) const;
> int update_incremental (svr4_info *info, CORE_ADDR debug_base,
> diff --git a/gdb/solib-target.c b/gdb/solib-target.c
> index ac965df20e94..0df8c6048aef 100644
> --- a/gdb/solib-target.c
> +++ b/gdb/solib-target.c
> @@ -232,7 +232,7 @@ solib_target_parse_libraries (const char *library)
> #endif
>
> owning_intrusive_list<solib>
> -target_solib_ops::current_sos () const
> +target_solib_ops::current_sos ()
> {
> owning_intrusive_list<solib> sos;
>
> diff --git a/gdb/solib-target.h b/gdb/solib-target.h
> index 0d2f0840aa63..abfa0b513742 100644
> --- a/gdb/solib-target.h
> +++ b/gdb/solib-target.h
> @@ -29,7 +29,7 @@ struct target_solib_ops : solib_ops
> using solib_ops::solib_ops;
>
> void relocate_section_addresses (solib &so, target_section *) const override;
> - owning_intrusive_list<solib> current_sos () const override;
> + owning_intrusive_list<solib> current_sos () override;
> bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
> };
>
> diff --git a/gdb/solib.c b/gdb/solib.c
> index d92d0dd9a3dd..ae41572b2b58 100644
> --- a/gdb/solib.c
> +++ b/gdb/solib.c
> @@ -438,7 +438,7 @@ solib_bfd_open (const char *pathname)
> }
>
> gdb_bfd_ref_ptr
> -solib_ops::bfd_open (const char *pathname) const
> +solib_ops::bfd_open (const char *pathname)
> {
> return solib_bfd_open (pathname);
> }
> @@ -689,7 +689,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;
> @@ -1289,7 +1289,7 @@ clear_solib (program_space *pspace)
> void
> solib_create_inferior_hook (int from_tty)
> {
> - if (const solib_ops *ops = current_program_space->solib_ops ();
> + if (solib_ops *ops = current_program_space->solib_ops ();
> ops != nullptr)
> ops->create_inferior_hook (from_tty);
> }
> @@ -1354,7 +1354,7 @@ update_solib_breakpoints (void)
> void
> handle_solib_event (void)
> {
> - if (const solib_ops *ops = current_program_space->solib_ops ();
> + if (solib_ops *ops = current_program_space->solib_ops ();
> ops != nullptr)
> ops->handle_event ();
>
> diff --git a/gdb/solib.h b/gdb/solib.h
> index 9e6c3f7346ee..5b5049f53435 100644
> --- a/gdb/solib.h
> +++ b/gdb/solib.h
> @@ -62,7 +62,7 @@ struct solib : intrusive_list_node<solib>
>
> OPS is the solib_ops implementation providing this solib. */
> explicit solib (lm_info_up lm_info, std::string original_name,
> - std::string name, const solib_ops &ops)
> + std::string name, solib_ops &ops)
> : lm_info (std::move (lm_info)),
> original_name (std::move (original_name)),
> name (std::move (name)),
> @@ -70,7 +70,7 @@ struct solib : intrusive_list_node<solib>
> {}
>
> /* 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
> @@ -131,7 +131,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. */
> @@ -173,7 +173,7 @@ struct solib_ops
> /* Target dependent code to run after child process fork.
>
> Defaults to no-op. */
> - virtual void create_inferior_hook (int from_tty) const {};
> + 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.
> @@ -182,7 +182,7 @@ 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. */
> - virtual owning_intrusive_list<solib> current_sos () const = 0;
> + 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.
> @@ -198,7 +198,7 @@ struct solib_ops
> { return false; };
>
> /* Find and open shared library binary file. */
> - virtual gdb_bfd_ref_ptr bfd_open (const char *pathname) const;
> + virtual gdb_bfd_ref_ptr bfd_open (const char *pathname);
>
> /* 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.
> @@ -226,7 +226,7 @@ struct solib_ops
> solib_add is called.
>
> Defaults to no-op. */
> - virtual void handle_event () const {};
> + 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
> diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
> index 78bf49b9c81f..261c38b35635 100644
> --- a/gdb/windows-tdep.c
> +++ b/gdb/windows-tdep.c
> @@ -828,7 +828,7 @@ struct windows_solib_ops : target_solib_ops
> {
> using target_solib_ops::target_solib_ops;
>
> - void create_inferior_hook (int from_tty) const override;
> + void create_inferior_hook (int from_tty) override;
> void iterate_over_objfiles_in_search_order
> (iterate_over_objfiles_in_search_order_cb_ftype cb,
> objfile *current_objfile) const override;
> @@ -845,7 +845,7 @@ make_windows_solib_ops (program_space *pspace)
> /* Implement the "solib_create_inferior_hook" solib_ops method. */
>
> void
> -windows_solib_ops::create_inferior_hook (int from_tty) const
> +windows_solib_ops::create_inferior_hook (int from_tty)
> {
> CORE_ADDR exec_base = 0;
>
> --
> 2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 5/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops
2026-06-08 20:00 ` [PATCH RESEND v2 05/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops Simon Marchi
@ 2026-07-06 17:09 ` Lancelot SIX
2026-07-08 16:08 ` Simon Marchi
0 siblings, 1 reply; 18+ messages in thread
From: Lancelot SIX @ 2026-07-06 17:09 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On Mon, Jun 08, 2026 at 04:00:29PM -0400, Simon Marchi wrote:
> Move the data currently stored in an inferior registry directly into
> rocm_solib_ops. This changes the storage of this data from "per
> inferior" to "per program-space", but it should be equivalent, since
> there is usually a 1:1 mapping between those two. The only time there
> isn't a 1:1 mapping is during a vfork, before the exec/exit, but no ROCm
> activity happens during that slice of time.
>
> Function rocm_update_solib_list becomes
> rocm_solib_ops::update_solib_list.
>
> Function rocm_bfd_iovec_open becomes rocm_solib_ops::bfd_iovec_open.
>
> Most of the changes consist of accessing the fields directly, instead of
> going through the solib_info object.
>
> The rocm_solib_ops constructor has to take an inferior as a parameter,
> rather than a program_space, because of the fd cache.
>
Hi Simon,
It seems that this patch introduces a use-after-free issue when handling
the "detach-on-fork on", "follow-fork-mode child" case (I see this with
the gdb.rocm/fork-exec-gpu-to-non-gpu.exp testcase with ASAN enabled).
When doing the case where we follow the child, we reach
rocm_solib_target_inferior_forked:
if (detach_on_fork && follow_child && fork_kind == TARGET_WAITKIND_FORKED)
{
auto rocm_ops_holder = child_inf->pspace->release_solib_ops ();
auto rocm_ops
= gdb::checked_static_cast<rocm_solib_ops *> (rocm_ops_holder.get ());
child_inf->pspace->set_solib_ops (rocm_ops->release_host_ops ());
}
On exit here of this block, the rocm_ops is deleted (calls
rocm_solib_ops::~rocm_solib_ops).
However, at this stage, if there are still solibs which were opened by
rocm_solib_ops, the next call to update_solib_list will lead to deleting
the solib which do not exist anymore (so far so good). Any file
rocm-solib still open at this point will eventually be closed when we
close the associated BFD. This calls into rocm_solib_fd_cache::close,
which tries to access the rocm_solib_fd_cache::m_cache field. However,
this is a reference to a rocm_solib_ops::m_fd_cache, which is now gone,
leading to a use after free.
This was not an issue until this patch because the fd_cache was owned by
the per-inferior rocm_solib_data. which is not going anywhere during the fork.
Best,
Lancelot.
> Change-Id: I386139008b6f0eca1d897fc35be18faa9d5a104e
> ---
> gdb/solib-rocm.c | 90 ++++++++++++++++++------------------------------
> 1 file changed, 33 insertions(+), 57 deletions(-)
>
> diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
> index 4032ada28a0f..618f4f5e7905 100644
> --- a/gdb/solib-rocm.c
> +++ b/gdb/solib-rocm.c
> @@ -181,30 +181,15 @@ struct rocm_so
> lm_info_svr4_up lm_info;
> };
>
> -struct solib_info
> -{
> - explicit solib_info (inferior *inf)
> - : fd_cache (inf)
> - {};
> -
> - /* List of code objects loaded into the inferior. */
> - std::vector<rocm_so> solib_list;
> -
> - /* Cache of opened FD in the inferior. */
> - rocm_solib_fd_cache fd_cache;
> -};
> -
> -/* Per-inferior data key. */
> -static const registry<inferior>::key<solib_info> rocm_solib_data;
> -
> /* 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 (program_space *pspace, solib_ops_up host_ops)
> - : solib_ops (pspace), m_host_ops (std::move (host_ops))
> + explicit rocm_solib_ops (inferior *inf, solib_ops_up host_ops)
> + : solib_ops (inf->pspace), m_host_ops (std::move (host_ops)),
> + m_fd_cache (inf)
> {
> gdb_assert (m_host_ops != nullptr);
> gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
> @@ -221,6 +206,10 @@ struct rocm_solib_ops : public solib_ops
> void relocate_section_addresses (solib &so, target_section *) const override;
> void handle_event () override;
>
> + /* This needs to be public because it is called from
> + rocm_solib_target_inferior_created. */
> + void update_solib_list ();
> +
> /* 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. */
> @@ -271,18 +260,17 @@ struct rocm_solib_ops : public solib_ops
> private:
> owning_intrusive_list<solib>
> solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
> + gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd, inferior *inferior);
>
> solib_ops_up m_host_ops;
> +
> + /* List of code objects loaded into the inferior. */
> + std::vector<rocm_so> m_solib_list;
> +
> + /* Cache of opened FD in the inferior. */
> + rocm_solib_fd_cache m_fd_cache;
> };
>
> -/* Fetch the solib_info data for INF. */
> -
> -static struct solib_info *
> -get_solib_info (inferior *inf)
> -{
> - return &rocm_solib_data.try_emplace (inf, inf);
> -}
> -
> /* Relocate section addresses. */
>
> void
> @@ -300,8 +288,6 @@ rocm_solib_ops::relocate_section_addresses (solib &so,
> sec->endaddr = sec->endaddr + li->l_addr;
> }
>
> -static void rocm_update_solib_list ();
> -
> void
> rocm_solib_ops::handle_event ()
> {
> @@ -313,7 +299,7 @@ rocm_solib_ops::handle_event ()
> previously loaded). */
> m_host_ops->handle_event ();
>
> - rocm_update_solib_list ();
> + this->update_solib_list ();
> }
>
> /* Create solib objects from rocm_so objects in SOS. */
> @@ -340,12 +326,10 @@ rocm_solib_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;
> -
> - if (dev_sos.empty ())
> + if (m_solib_list.empty ())
> return sos;
>
> - owning_intrusive_list<solib> dev_solibs = solibs_from_rocm_sos (dev_sos);
> + owning_intrusive_list<solib> dev_solibs = solibs_from_rocm_sos (m_solib_list);
>
> if (sos.empty ())
> return dev_solibs;
> @@ -525,8 +509,8 @@ rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
>
> } /* anonymous namespace */
>
> -static gdb_bfd_iovec_base *
> -rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
> +gdb_bfd_iovec_base *
> +rocm_solib_ops::bfd_iovec_open (bfd *abfd, inferior *inferior)
> {
> std::string_view uri (bfd_get_filename (abfd));
> std::string_view protocol_delim = "://";
> @@ -635,10 +619,9 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior)
> && decoded_path[3] == '/')
> decoded_path.erase (0, 1);
>
> - auto info = get_solib_info (inferior);
> fileio_error target_errno;
> rocm_solib_fd_cache::cached_target_fd fd
> - = info->fd_cache.open (decoded_path, &target_errno);
> + = m_fd_cache.open (decoded_path, &target_errno);
>
> if (fd.fd () == target_fd::INVALID)
> {
> @@ -698,9 +681,9 @@ rocm_solib_ops::bfd_open (const char *pathname)
> if (strstr (pathname, "://") == nullptr)
> return m_host_ops->bfd_open (pathname);
>
> - auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base *
> + auto open = [this] (bfd *nbfd)
> {
> - return rocm_bfd_iovec_open (nbfd, current_inferior ());
> + return this->bfd_iovec_open (nbfd, current_inferior ());
> };
>
> gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
> @@ -782,13 +765,12 @@ rocm_solib_ops::bfd_open (const char *pathname)
> void
> rocm_solib_ops::create_inferior_hook (int from_tty)
> {
> - get_solib_info (current_inferior ())->solib_list.clear ();
> -
> + m_solib_list.clear ();
> m_host_ops->create_inferior_hook (from_tty);
> }
>
> -static void
> -rocm_update_solib_list ()
> +void
> +rocm_solib_ops::update_solib_list ()
> {
> inferior *inf = current_inferior ();
>
> @@ -796,10 +778,7 @@ rocm_update_solib_list ()
> if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
> return;
>
> - solib_info *info = get_solib_info (inf);
> -
> - info->solib_list.clear ();
> - std::vector<rocm_so> &sos = info->solib_list;
> + m_solib_list.clear ();
>
> amd_dbgapi_code_object_id_t *code_object_list;
> size_t count;
> @@ -846,7 +825,8 @@ rocm_update_solib_list ()
> std::string unique_name
> = string_printf ("code_object_%ld", code_object_list[i].handle);
>
> - sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li));
> + m_solib_list.emplace_back (uri_bytes, std::move (unique_name),
> + std::move (li));
> }
> }
>
> @@ -857,17 +837,15 @@ rocm_solib_target_inferior_created (inferior *inf)
> if (inf->vfork_parent != nullptr)
> return;
>
> - get_solib_info (inf)->solib_list.clear ();
> -
> auto prev_ops = inf->pspace->release_solib_ops ();
> - auto rocm_ops
> - = std::make_unique<rocm_solib_ops> (inf->pspace, std::move (prev_ops));
> + auto rocm_ops = std::make_unique<rocm_solib_ops> (inf, std::move (prev_ops));
> inf->pspace->set_solib_ops (std::move (rocm_ops));
>
> - rocm_update_solib_list ();
> + gdb::checked_static_cast<rocm_solib_ops *>
> + (inf->pspace->solib_ops ())->update_solib_list ();
>
> /* Force GDB to reload the solibs. */
> - current_inferior ()->pspace->clear_solib_cache ();
> + inf->pspace->clear_solib_cache ();
> solib_add (nullptr, 0, auto_solib_add);
> }
>
> @@ -882,10 +860,8 @@ rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf)
> auto pspace = follow_inf->pspace;
> auto prev_ops = pspace->release_solib_ops ();
> auto rocm_ops
> - = std::make_unique<rocm_solib_ops> (pspace, std::move (prev_ops));
> + = std::make_unique<rocm_solib_ops> (follow_inf, std::move (prev_ops));
> pspace->set_solib_ops (std::move (rocm_ops));
> -
> - get_solib_info (exec_inf)->solib_list.clear ();
> }
>
> static void
> --
> 2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 6/10] gdb/solib-rocm: save inferior in rocm_solib_ops
2026-06-08 20:00 ` [PATCH RESEND v2 06/10] gdb/solib-rocm: save inferior in rocm_solib_ops Simon Marchi
@ 2026-07-06 17:10 ` Lancelot SIX
0 siblings, 0 replies; 18+ messages in thread
From: Lancelot SIX @ 2026-07-06 17:10 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
On Mon, Jun 08, 2026 at 04:00:30PM -0400, Simon Marchi wrote:
> Storing the inferior in rocm_solib_ops lets us avoid some calls to
> current_inferior().
>
Hi Simon,
This looks good to me. Thanks.
Approved-by: Lancelot Six <lancelot.six@amd.com> (amdgpu)
Best,
Lancelot.
> Change-Id: Icf36bbef031eda7b776cd735a91a2ff918697dd3
> ---
> gdb/solib-rocm.c | 17 +++++++++--------
> 1 file changed, 9 insertions(+), 8 deletions(-)
>
> diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c
> index 618f4f5e7905..0a284dfb0a56 100644
> --- a/gdb/solib-rocm.c
> +++ b/gdb/solib-rocm.c
> @@ -189,7 +189,7 @@ struct rocm_solib_ops : public solib_ops
> in order to provide support for ROCm code objects. */
> explicit rocm_solib_ops (inferior *inf, solib_ops_up host_ops)
> : solib_ops (inf->pspace), m_host_ops (std::move (host_ops)),
> - m_fd_cache (inf)
> + m_inf (inf), m_fd_cache (inf)
> {
> gdb_assert (m_host_ops != nullptr);
> gdb_assert (dynamic_cast<rocm_solib_ops *> (m_host_ops.get ()) == nullptr);
> @@ -260,10 +260,13 @@ struct rocm_solib_ops : public solib_ops
> private:
> owning_intrusive_list<solib>
> solibs_from_rocm_sos (const std::vector<rocm_so> &sos);
> - gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd, inferior *inferior);
> + gdb_bfd_iovec_base *bfd_iovec_open (bfd *abfd);
>
> solib_ops_up m_host_ops;
>
> + /* Inferior this rocm_solib_ops is for. */
> + inferior *m_inf;
> +
> /* List of code objects loaded into the inferior. */
> std::vector<rocm_so> m_solib_list;
>
> @@ -510,7 +513,7 @@ rocm_code_object_stream_memory::read (bfd *, void *buf, file_ptr size,
> } /* anonymous namespace */
>
> gdb_bfd_iovec_base *
> -rocm_solib_ops::bfd_iovec_open (bfd *abfd, inferior *inferior)
> +rocm_solib_ops::bfd_iovec_open (bfd *abfd)
> {
> std::string_view uri (bfd_get_filename (abfd));
> std::string_view protocol_delim = "://";
> @@ -637,7 +640,7 @@ rocm_solib_ops::bfd_iovec_open (bfd *abfd, inferior *inferior)
> if (protocol == "memory")
> {
> ULONGEST pid = try_strtoulst (path);
> - if (pid != inferior->pid)
> + if (pid != m_inf->pid)
> {
> warning (_("`%s': code object is from another inferior"),
> std::string (uri).c_str ());
> @@ -683,7 +686,7 @@ rocm_solib_ops::bfd_open (const char *pathname)
>
> auto open = [this] (bfd *nbfd)
> {
> - return this->bfd_iovec_open (nbfd, current_inferior ());
> + return this->bfd_iovec_open (nbfd);
> };
>
> gdb_bfd_ref_ptr abfd = gdb_bfd_openr_iovec (pathname, "elf64-amdgcn", open);
> @@ -772,9 +775,7 @@ rocm_solib_ops::create_inferior_hook (int from_tty)
> void
> rocm_solib_ops::update_solib_list ()
> {
> - inferior *inf = current_inferior ();
> -
> - amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (inf);
> + amd_dbgapi_process_id_t process_id = get_amd_dbgapi_process_id (m_inf);
> if (process_id.handle == AMD_DBGAPI_PROCESS_NONE.handle)
> return;
>
> --
> 2.54.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime
2026-07-06 17:08 ` [PATCH v2 3/10] " Lancelot SIX
@ 2026-07-07 20:48 ` Simon Marchi
0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-07-07 20:48 UTC (permalink / raw)
To: Lancelot SIX, Simon Marchi, gdb-patches
On 2026-07-06 13:08, Lancelot SIX wrote:
> On Mon, Jun 08, 2026 at 04:00:27PM -0400, Simon Marchi wrote:
>> Make the management of the cached fds automatic.
>>
>> Change rocm_solib_fd_cache::open to return a cached_target_fd, an RAII
>> type that calls rocm_solib_fd_cache::close on destruction.
>> cached_target_fd holds the actual fd and a reference to the cache it
>> comes from.
>>
>> Change rocm_code_object_stream_file to hold a cached_target_fd and
>> remove the explicit destructor.
>>
>> This is not essential, I just thought it would be nice.
>
> Hi Simon,
>
> This overall looks good. Just have one minor nits below. Other than that,
>
> Approved-by: Lancelot Six <lancelot.six@amd.com> (amdgpu)
Thanks, fixed both.
Simon
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 5/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops
2026-07-06 17:09 ` [PATCH v2 5/10] " Lancelot SIX
@ 2026-07-08 16:08 ` Simon Marchi
0 siblings, 0 replies; 18+ messages in thread
From: Simon Marchi @ 2026-07-08 16:08 UTC (permalink / raw)
To: Lancelot SIX, Simon Marchi, gdb-patches
On 7/6/26 1:09 PM, Lancelot SIX wrote:
> On Mon, Jun 08, 2026 at 04:00:29PM -0400, Simon Marchi wrote:
>> Move the data currently stored in an inferior registry directly into
>> rocm_solib_ops. This changes the storage of this data from "per
>> inferior" to "per program-space", but it should be equivalent, since
>> there is usually a 1:1 mapping between those two. The only time there
>> isn't a 1:1 mapping is during a vfork, before the exec/exit, but no ROCm
>> activity happens during that slice of time.
>>
>> Function rocm_update_solib_list becomes
>> rocm_solib_ops::update_solib_list.
>>
>> Function rocm_bfd_iovec_open becomes rocm_solib_ops::bfd_iovec_open.
>>
>> Most of the changes consist of accessing the fields directly, instead of
>> going through the solib_info object.
>>
>> The rocm_solib_ops constructor has to take an inferior as a parameter,
>> rather than a program_space, because of the fd cache.
>>
>
> Hi Simon,
>
> It seems that this patch introduces a use-after-free issue when handling
> the "detach-on-fork on", "follow-fork-mode child" case (I see this with
> the gdb.rocm/fork-exec-gpu-to-non-gpu.exp testcase with ASAN enabled).
>
> When doing the case where we follow the child, we reach
> rocm_solib_target_inferior_forked:
>
> if (detach_on_fork && follow_child && fork_kind == TARGET_WAITKIND_FORKED)
> {
> auto rocm_ops_holder = child_inf->pspace->release_solib_ops ();
> auto rocm_ops
> = gdb::checked_static_cast<rocm_solib_ops *> (rocm_ops_holder.get ());
> child_inf->pspace->set_solib_ops (rocm_ops->release_host_ops ());
> }
>
> On exit here of this block, the rocm_ops is deleted (calls
> rocm_solib_ops::~rocm_solib_ops).
>
> However, at this stage, if there are still solibs which were opened by
> rocm_solib_ops, the next call to update_solib_list will lead to deleting
> the solib which do not exist anymore (so far so good). Any file
> rocm-solib still open at this point will eventually be closed when we
> close the associated BFD. This calls into rocm_solib_fd_cache::close,
> which tries to access the rocm_solib_fd_cache::m_cache field. However,
> this is a reference to a rocm_solib_ops::m_fd_cache, which is now gone,
> leading to a use after free.
>
> This was not an issue until this patch because the fd_cache was owned by
> the per-inferior rocm_solib_data. which is not going anywhere during the fork.
Ok, thanks for the analysis. I didn't spot this because I didn't fully
test each commit. The issue goes away at the main multiple solib ops
patch, because it adds the behavior of removing all the solibs owned by
a given solib_ops when removing that solib_ops. Having known this, I
would perhaps have kept this cleanup for later. But now, the subsequent
patches are based on this one, and removing it is not trivial and risks
introducing more issues. So the fix I propose is:
- Move the remove_solib patch before this one, this is
trivial. Make remove_solib public from the start (it would become
public in the last patch anyway).
- Change this patch to call remove_solib on the solibs owned by the
rocm_solib_ops. This mimics what the last patch does.
The end result of the series is the same, it just changes the
intermediary states. I would send a new version of the series with
those changes.
Simon
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-07-08 16:09 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-08 20:00 [PATCH RESEND v2 00/10] Multiple solib_ops in a program_space Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 01/10] gdb/solib-rocm: assert that host ops isn't rocm_solib_ops Simon Marchi
2026-07-06 17:08 ` [PATCH v2 1/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 02/10] gdb/solib-rocm: pass reference to cache to rocm_code_object_stream_file Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 03/10] gdb/solib-rocm: add cached_target_fd to manage cached fd lifetime Simon Marchi
2026-07-06 17:08 ` [PATCH v2 3/10] " Lancelot SIX
2026-07-07 20:48 ` Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 04/10] gdb: de-constify some methods of solib_ops Simon Marchi
2026-07-06 17:09 ` [PATCH v2 4/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 05/10] gdb/solib-rocm: move per-inferior data to rocm_solib_ops Simon Marchi
2026-07-06 17:09 ` [PATCH v2 5/10] " Lancelot SIX
2026-07-08 16:08 ` Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 06/10] gdb/solib-rocm: save inferior in rocm_solib_ops Simon Marchi
2026-07-06 17:10 ` [PATCH v2 6/10] " Lancelot SIX
2026-06-08 20:00 ` [PATCH RESEND v2 07/10] gdb/solib: add remove_solib function Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 08/10] gdb: add objfile -> solib backlink Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 09/10] gdb: change default objfile iteration order to start with current objfile Simon Marchi
2026-06-08 20:00 ` [PATCH RESEND v2 10/10] gdb: multiple solib_ops per program space Simon Marchi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox