From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id wU9bDSbHTmpG0iwAWB0awg (envelope-from ) for ; Wed, 08 Jul 2026 17:54:46 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=DR+n3rcc; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 2F98B1E098; Wed, 08 Jul 2026 17:54:46 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=unavailable autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 88EAA1E04F for ; Wed, 08 Jul 2026 17:54:43 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4FA744BA2E13 for ; Wed, 8 Jul 2026 21:54:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4FA744BA2E13 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=DR+n3rcc Received: from smtp.polymtl.ca (smtp.polymtl.ca [132.207.4.11]) by sourceware.org (Postfix) with ESMTPS id 5B7D94BA2E11 for ; Wed, 8 Jul 2026 21:54:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5B7D94BA2E11 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=polymtl.ca Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=polymtl.ca ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5B7D94BA2E11 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=132.207.4.11 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1783547642; cv=none; b=siNQ45SPSpUEkaKrw+vp7SVZfCD++7J4JGMT3J32sry5R/Fym3zE3uI1bPFSuxnibWxcTHFzuCNqbUGTISDwBzpQlU/3dC9oSsHzbkD+jfKaeIJMjIC4xOXTaNG0rUz+QHEaYalpSREkH4sUdN0sDu9stLKxWNuXwjTmPp84ouU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1783547642; c=relaxed/simple; bh=ytugZDi460gjOT6aepdEdJfG5XWndPiw67tgSpnFUW0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=nwji9cqLEjeBIhu6kWnlIU+YeEtbqghRHSoRE3q1/rceXNXLGY4rfgHuWv8bhUi2/qP95i84fYrV2tDcgZA5x0AnK3otKnu0+DSeYwaTkU1SCiwUw1qnPu1Qi4zP/ybZdCW2+iLbB7xePt3uZ3gZF/CWrH5aON4WtSyOnseoF/o= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=polymtl.ca header.i=@polymtl.ca header.a=rsa-sha256 header.s=oct2025 header.b=DR+n3rcc DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5B7D94BA2E11 Received: from simark.ca (simark.ca [158.69.221.121]) (authenticated bits=0) by smtp.polymtl.ca (8.14.7/8.14.7) with ESMTP id 668Lrroj118058 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 8 Jul 2026 17:53:58 -0400 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp.polymtl.ca 668Lrroj118058 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=polymtl.ca; s=oct2025; t=1783547639; bh=KeqHxKT5Iy0DTtBbT52nI+bjOcjneDrAmlnEaeWBA4Q=; h=From:To:Cc:Subject:Date:In-Reply-To:From; b=DR+n3rccBGtLojqeI64irrpFEbDPLLg7eDMkoVlJ7Al7VEcKXcaJ99f2rj42GmVb8 GUB2ynJ1bqwOOQPG1sDUYfuLUCTrXpvd/r+S0MAXnzumPkCVFHbmoIMNzIUboJb9kJ WLI0qW4RO67N69LBWnxLoh4fmklAJ3MJhuV+EGfjhl0nqqYSq1IxVBEFalfyZ6u8XI mGIcls9qaafUg50mR0MJAT4xZauyyXCeVKNmRLRa3m6eJi7fHn1lHq8q9J6GzMpaTn MiACOl6+ZbOKF6XHlnt3pUZGftxlT+o/3+g+IS+kNqVRzHCJ9/43FAKefqZk76wd8z EvZ3BGmLPABoQ== Received: by simark.ca (Postfix) id 88A0A1E098; Wed, 08 Jul 2026 17:53:53 -0400 (EDT) From: simon.marchi@polymtl.ca To: gdb-patches@sourceware.org Cc: Tom Tromey , Lancelot SIX , Simon Marchi Subject: [PATCH v3 10/10] gdb: multiple solib_ops per program space Date: Wed, 8 Jul 2026 17:51:42 -0400 Message-ID: <20260708215145.93134-11-simon.marchi@polymtl.ca> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260708215145.93134-1-simon.marchi@polymtl.ca> References: <20260708215145.93134-1-simon.marchi@polymtl.ca> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Poly-FromMTA: (simark.ca [158.69.221.121]) at Wed, 8 Jul 2026 21:53:53 +0000 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: Simon Marchi 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 --- 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 | 182 +++---------- gdb/solib-svr4.c | 32 ++- gdb/solib-svr4.h | 6 +- gdb/solib-target.c | 2 +- gdb/solib.c | 251 ++++++++++-------- gdb/solib.h | 20 +- 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, 939 insertions(+), 304 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 ab3fde11cf94..5372bc8e53c1 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 + solib_ops_type &add_solib_ops (std::unique_ptr ops) { - gdb_assert (m_solib_ops == nullptr); - m_solib_ops = std::move (ops); + gdb_assert (this->find_solib_ops () == 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. + + Remove all solibs from the program space. */ + void clear_solib_ops (); + + /* Remove OPS from the list of solib_ops of this program space. + + OPS must be present in the solib_ops list. - /* Unset and return this program space's solib provider. */ - solib_ops_up release_solib_ops () - { return std::move (m_solib_ops); } + Remove all solibs created by OPS from the program space. */ + void remove_solib_ops (const solib_ops &ops); - /* Get this program space's solib provider. */ - struct solib_ops *solib_ops () const - { return m_solib_ops.get (); } + /* Return a view of the solib providers for this program space. */ + std::vector &solib_ops () + { return m_solib_ops; } + + /* Find an solib_ops instance of type SOLIB_OPS_TYPE. + + Return nullptr if not found. */ + template + solib_ops_type *find_solib_ops () + { + for (auto &ops : this->solib_ops ()) + if (auto ret = dynamic_cast (ops.get ()); + ret != nullptr) + return ret; + + return nullptr; + } /* Return the list of all the solibs in this program space. */ owning_intrusive_list &solibs () @@ -401,8 +426,8 @@ struct program_space /* All known objfiles are kept in a linked list. */ owning_intrusive_list 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 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 (pspace); + return std::make_unique (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 (pspace); + return std::make_unique (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 (pspace); + return std::make_unique (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 (pspace); + return std::make_unique (pspace, true); } /* FR-V pointers are four bytes wide. */ diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c index 6f8ba8c00ac4..61b3dcaf716a 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 (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 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 find_solib_addr (solib &so) const override - { return m_host_ops->find_solib_addr (so); } - - bool supports_namespaces () const override - { return m_host_ops->supports_namespaces (); } - - 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 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 solibs_from_rocm_sos (const std::vector &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 (so.lm_info.get ()); - sec->addr = sec->addr + li->l_addr; - sec->endaddr = sec->endaddr + li->l_addr; + auto &li = gdb::checked_static_cast (*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 &sos) owning_intrusive_list dst; for (const rocm_so &so : sos) - dst.emplace_back (std::make_unique (*so.lm_info), + dst.emplace_back (std::make_unique (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 &sos) owning_intrusive_list rocm_solib_ops::current_sos () { - /* First, retrieve the host-side shared library list. */ - owning_intrusive_list sos = m_host_ops->current_sos (); - - /* Then, the device-side shared library list. */ - if (m_solib_list.empty ()) - return sos; - - owning_intrusive_list 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 uri_bytes_holder (uri_bytes); - /* Pass a dummy debug base. */ - lm_info_svr4_up li = std::make_unique (-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 (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 (inf)); - gdb::checked_static_cast - (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 (follow_inf, std::move (prev_ops)); - pspace->set_solib_ops (std::move (rocm_ops)); + follow_inf->pspace->add_solib_ops + (std::make_unique (follow_inf)); } static void @@ -872,31 +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. - - Remove solibs created by the rocm_solib_ops, since they contain back - references to the rocm_solib_ops (namely, to the fd cache). */ - auto &solibs = child_inf->pspace->solibs (); - auto rocm_ops - = gdb::checked_static_cast - (child_inf->pspace->solib_ops ()); - - for (auto solibs_it = solibs.begin (); solibs_it != solibs.end ();) - { - if (&solibs_it->ops () != rocm_ops) - { - ++solibs_it; - continue; - } - - solibs_it = remove_solib (child_inf->pspace, solibs_it); - } - - auto rocm_ops_holder = child_inf->pspace->release_solib_ops (); - 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 (); + 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 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 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 (pspace); + return std::make_unique (pspace, true); } diff --git a/gdb/solib.c b/gdb/solib.c index 6ed56aaf5159..ef5d20247517 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -21,6 +21,7 @@ #include #include "exceptions.h" #include "extract-store-integer.h" +#include "progspace.h" #include "symtab.h" #include "bfd.h" #include "build-id.h" @@ -446,17 +447,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 @@ -742,11 +764,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) @@ -761,7 +778,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) { @@ -796,7 +815,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 inferior = ops->current_sos (); + owning_intrusive_list inferior; + for (auto &ops : current_program_space->solib_ops ()) + inferior.splice (ops->current_sos ()); + owning_intrusive_list::iterator gdb_iter = current_program_space->solibs ().begin (); while (gdb_iter != current_program_space->solibs ().end ()) @@ -806,8 +828,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 @@ -1024,25 +1051,28 @@ print_solib_list_table (std::vector 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 (), @@ -1080,7 +1110,11 @@ print_solib_list_table (std::vector 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) { @@ -1163,81 +1197,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 (); + pattern = skip_spaces (pattern); - /* 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.")); + /* Will be set to true if at least one solib_ops in the program space + supports namespaces. */ + bool one_solib_ops_supports_namespaces = false; - struct ui_out *uiout = current_uiout; - std::vector>> all_solibs_to_print; + for (auto &ops : current_program_space->solib_ops ()) + { + if (!ops->supports_namespaces ()) + continue; - pattern = skip_spaces (pattern); + one_solib_ops_supports_namespaces = true; - if (pattern == nullptr || pattern[0] == '\0') - { - uiout->message (_("There are %d linker namespaces loaded.\n"), - ops->num_active_namespaces ()); + struct ui_out *uiout = current_uiout; + std::vector>> + all_solibs_to_print; - int printed = 0; - for (int i = 0; printed < ops->num_active_namespaces (); i++) + if (pattern == nullptr || pattern[0] == '\0') { - std::vector 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 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); - } - - all_solibs_to_print.push_back - (std::make_pair (ns, ops->get_solibs_in_ns (ns))); - } + 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); + } - for (const auto &[ns, solibs_to_print] : all_solibs_to_print) - { - uiout->message ("\n"); + all_solibs_to_print.push_back + (std::make_pair (ns, ops->get_solibs_in_ns (ns))); + } - 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 () == 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); + 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); + 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. */ @@ -1285,9 +1330,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. */ @@ -1306,8 +1353,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); } @@ -1319,8 +1365,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); } @@ -1329,9 +1374,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. */ @@ -1373,9 +1420,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 (); } @@ -1384,8 +1429,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 (); @@ -1483,8 +1527,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 @@ -1816,11 +1859,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 903b9d7b2a86..4ca3c205ef29 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -140,8 +140,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; @@ -271,21 +271,29 @@ struct solib_ops virtual std::vector 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. */ 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 . */ + +#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 +#include +#include +#include +#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, 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; + +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 (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 \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 . + +# 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" "" + gdb_test "print func_host_shared_lib_1" "" + gdb_test "print func_host_shared_lib_2" "" + gdb_test "print func_device_code_object_1" "" + gdb_test "print func_device_code_object_2" "" + + # Functions defined in either both host shlibs or both code objects. + gdb_test "print func_host_shared_lib" "" + gdb_test "print func_device_code_object" "" + + # Functions defined in one host shlib and one code object. + gdb_test "print func_1" "" + gdb_test "print func_2" "" + + # Functions defined in main program and one objfile. + gdb_test "print func_main_shared_lib_1" "" + gdb_test "print func_main_shared_lib_2" "" + gdb_test "print func_main_code_object_1" "" + gdb_test "print 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 (pspace); + return std::make_unique (pspace, true); } /* Implement the "solib_create_inferior_hook" solib_ops method. */ -- 2.55.0