From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id XTtoNJG52mdStxQAWB0awg (envelope-from ) for ; Wed, 19 Mar 2025 08:33:21 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=O2bWGfz7; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id C566E1E100; Wed, 19 Mar 2025 08:33:21 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 281BB1E0C0 for ; Wed, 19 Mar 2025 08:33:20 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7DD95385800F for ; Wed, 19 Mar 2025 12:33:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7DD95385800F Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=O2bWGfz7 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id E5869385841F for ; Wed, 19 Mar 2025 12:32:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E5869385841F Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E5869385841F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1742387545; cv=none; b=rmHiD9tn0nwdKkrDc3Vg549I8cBiHw7sfNutsL0+4/xX1PMnt2m+4S1kO723hmFNKikMgCkZmRRu7Xl5DedbkmY4IW9TW+AKvAygdg+azxpyUYgN7lYN2L3H3aXKjuPRDmG5qbV+4d4GNqHPO3D0k5TsV7qZ14zuXnW7cDMEnZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1742387545; c=relaxed/simple; bh=b5ba/ToWmCuHmalOcQDQb1KaN4RL8EWerYezKFr2Dng=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=RtzyGQLA7tkHDU39q8OVd24HFNJERF2Y04X83wBzhhKVy7BYRoEEFjjK/rfLEb1LXHRNySYI7JxIFwU98ghnUMd2HuKOO15jNDjdWyWbr0Y3MiN6ieehrzdxK9C1kx1l5Bmm8IPi6QKIuhjad+sH3Qyq7ziCXTmsyWow5Y6rx3o= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E5869385841F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1742387544; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=JeT8PhBmWmZiOsVEwDvr7oo4LHKOWhU/qZomEFWIwtQ=; b=O2bWGfz7re/T7oasdC4dV+t5M+lF0r1VotOnmY/lWRXtQsTIgXXmDrtvQTVViMTCeLpjae 0FIu4FvDsQs3YVkb+IMxr5G7dTtBLwKSABsaq1laV87q0WKiAC9b3+fpkZ3u9SaJHRlOKf 7BIDRqgmwia1SQ9KKfmljLpFSMJ8ZsM= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-280-OcTl3luGO-CcbxJlOJvr2w-1; Wed, 19 Mar 2025 08:32:21 -0400 X-MC-Unique: OcTl3luGO-CcbxJlOJvr2w-1 X-Mimecast-MFC-AGG-ID: OcTl3luGO-CcbxJlOJvr2w_1742387541 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0312119560A1 for ; Wed, 19 Mar 2025 12:32:21 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.96.134.102]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4B9041828A9C; Wed, 19 Mar 2025 12:32:18 +0000 (UTC) From: Guinevere Larsen To: gdb-patches@sourceware.org Cc: Guinevere Larsen Subject: [PATCH v2] gdb: Introduce user-friendly namespace identifier for "info shared" Date: Wed, 19 Mar 2025 09:30:36 -0300 Message-ID: <20250319123035.1563282-2-guinevere@redhat.com> In-Reply-To: <20250313170004.3362207-2-guinevere@redhat.com> References: <20250313170004.3362207-2-guinevere@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: s9FMheMZx-BGxS7VcSoSg52yhcvcqha_31OuXHWgAcs_1742387541 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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 GDB has had basic support for linkage namespaces for some time already, but only in the sense of managing multiple copies of the same shared object being loaded, and a very fragile way to find the correct copy of a symbol (see PR shlibs/32054). This commit is the first step in improving the user experience around multiple namespace support. It introduces a user-friendly identifier for namespaces, in the format #, that will keep consistent between dlmopen and dlclose calls. The plan is for this identifier to be usable in expressions like `print #1::var` to find a specific instance of a symbol, and so the identifier must not be a valid C++ or Ada namespace identifier, otherwise disambiguation becomes a problem. Support for those expressions has not been implemented yet, it is only mentioned to explain why the identifier looks like this. The namespace identifiers are stored via a vector inside svr4_info object. The vector stores the address of the r_debug objects used by glibc to identify each namespace, and the user-friendly ID is the index of the r_debug in the vector. This commit also introduces a set storing the indices of active namespaces. The glibc I used to develop this patch (glibc 2.40 on Fedora 41) doesn't allow an SO to be loaded into a deactivated namespace, and requesting a new namespace when a namespace was previously closed will reuse that namespace. Because of how this is implemented, this patch lets GDB easily track the exact namespace IDs that the inferior will see. Finally, two new solib_ops function pointers were added, find_solib_ns and num_active_namespaces, to allow code outside of solib-svr4 to find and use the namespace identifiers and the number of namespaces, respectively. As a sanity check, the command `info sharedlibrary` has been changed to display the namespace identifier when the inferior has more than one active namespace. With this final change, a couple of tests had to be tweaked to handle the possible new column, and a new test has been created to make sure that the column appears and disappears as needed, and that GDB can track the value of the LMID for namespaces. --- Changes for v2: * made it so the new column in "info shared" only shows up when multiple namespaces are active * changes NEWS and docs to reflect that * Added a test for this functionality --- gdb/NEWS | 5 + gdb/doc/gdb.texinfo | 5 + gdb/solib-svr4.c | 108 +++++++++++++++++-- gdb/solib.c | 27 ++++- gdb/solist.h | 14 +++ gdb/testsuite/gdb.base/attach-pie-noexec.exp | 4 +- gdb/testsuite/gdb.base/dlmopen-ns-ids-lib.c | 28 +++++ gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c | 54 ++++++++++ gdb/testsuite/gdb.base/dlmopen-ns-ids.exp | 108 +++++++++++++++++++ gdb/testsuite/gdb.base/dlmopen.exp | 8 +- gdb/testsuite/gdb.mi/mi-dlmopen.exp | 6 +- 11 files changed, 350 insertions(+), 17 deletions(-) create mode 100644 gdb/testsuite/gdb.base/dlmopen-ns-ids-lib.c create mode 100644 gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c create mode 100644 gdb/testsuite/gdb.base/dlmopen-ns-ids.exp diff --git a/gdb/NEWS b/gdb/NEWS index 0aac7a7b13a..33918b142de 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -31,6 +31,11 @@ a -h or --help option, which prints each options and a brief description. +* On systems that support linkage namespaces, the output of the command + "info sharedlibraries" may add one more column, NS, which identifies the + namespace into which the library was loaded, if more than one namespace + is active. + * New commands maintenance check psymtabs diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 473431011d1..2350d3a238f 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -22166,6 +22166,11 @@ Print the names of the shared libraries which are currently loaded that match @var{regex}. If @var{regex} is omitted then print all shared libraries that are loaded. +On systems that support linkage namespaces, the output includes an +additional column @code{NS} if the inferior has more than one active +namespace when the command is used. This column the linkage namespace +that the shared library was loaded into. + @kindex info dll @item info dll @var{regex} This is an alias of @code{info sharedlibrary}. diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 8378ecaff40..c631eb4c770 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -405,11 +405,54 @@ struct svr4_info The special entry zero is reserved for a linear list to support gdbstubs that do not support namespaces. */ std::map> solib_lists; + + /* Mapping between r_debug[_ext] addresses and a user-friendly + identifier for the namespace. A vector is used to make it + easy to assign new internal IDs to namespaces. + + For gdbservers that don't support namespaces, the first (and only) + entry of the vector will be 0. + + A note on consistency. We can't make the IDs be consistent before + and after the initial relocation of the inferior (when the global + _r_debug is relocated, as mentioned in the previous comment). It is + likely that this is a non-issue, since the inferior can't have called + dlmopen yet, but I think it is worth noting. + + The only issue I am aware at this point is that, if when parsing an + XML file, we read an LMID that given by an XML file (and read in + library_list_start_library) is the identifier obtained with dlinfo + instead of the address of r_debug[_ext], and after attaching the + inferior adds another SO to that namespace, we might double-count it + since we won't have access to the LMID later on. However, this is + already a problem with the existing solib_lists code. */ + std::vector namespace_id; + + /* This identifies which namespaces are active. A namespace is considered + active when there is at least one shared object loaded into it. */ + std::set active_namespaces; }; /* Per-program-space data key. */ static const registry::key solib_svr4_pspace_data; +/* Check if the lmid address is already assigned an ID in the svr4_info, + and if not, assign it one and add it to the list of known namespaces. */ +static void +svr4_maybe_add_namespace (svr4_info *info, CORE_ADDR lmid) +{ + int i; + for (i = 0; i < info->namespace_id.size (); i++) + { + if (info->namespace_id[i] == lmid) + break; + } + if (i == info->namespace_id.size ()) + info->namespace_id.push_back (lmid); + + info->active_namespaces.insert (i); +} + /* Return whether DEBUG_BASE is the default namespace of INFO. */ static bool @@ -1041,14 +1084,18 @@ library_list_start_library (struct gdb_xml_parser *parser, /* Older versions did not supply lmid. Put the element into the flat list of the special namespace zero in that case. */ gdb_xml_value *at_lmid = xml_find_attribute (attributes, "lmid"); + svr4_info *info = get_svr4_info (current_program_space); if (at_lmid == nullptr) - solist = list->cur_list; + { + solist = list->cur_list; + svr4_maybe_add_namespace (info, 0); + } else { ULONGEST lmid = *(ULONGEST *) at_lmid->value.get (); solist = &list->solib_lists[lmid]; + svr4_maybe_add_namespace (info, lmid); } - solist->emplace_back (name, std::move (li)); } @@ -1286,6 +1333,8 @@ svr4_current_sos_direct (struct svr4_info *info) /* Remove any old libraries. We're going to read them back in again. */ info->solib_lists.clear (); + info->active_namespaces.clear (); + /* Fall back to manual examination of the target if the packet is not supported or gdbserver failed to find DT_DEBUG. gdb.server/solib-list.exp tests a case where gdbserver cannot find the shared libraries list while @@ -1333,7 +1382,10 @@ svr4_current_sos_direct (struct svr4_info *info) ignore_first = true; auto cleanup = make_scope_exit ([info] () - { info->solib_lists.clear (); }); + { + info->solib_lists.clear (); + info->active_namespaces.clear (); + }); /* Collect the sos in each namespace. */ CORE_ADDR debug_base = info->debug_base; @@ -1343,8 +1395,11 @@ svr4_current_sos_direct (struct svr4_info *info) /* Walk the inferior's link map list, and build our so_list list. */ lm = solib_svr4_r_map (debug_base); if (lm != 0) - svr4_read_so_list (info, lm, 0, info->solib_lists[debug_base], - ignore_first); + { + svr4_maybe_add_namespace (info, debug_base); + svr4_read_so_list (info, lm, 0, info->solib_lists[debug_base], + ignore_first); + } } /* On Solaris, the dynamic linker is not in the normal list of @@ -1361,8 +1416,11 @@ svr4_current_sos_direct (struct svr4_info *info) { /* Add the dynamic linker's namespace unless we already did. */ if (info->solib_lists.find (debug_base) == info->solib_lists.end ()) - svr4_read_so_list (info, debug_base, 0, info->solib_lists[debug_base], - 0); + { + svr4_maybe_add_namespace (info, debug_base); + svr4_read_so_list (info, debug_base, 0, info->solib_lists[debug_base], + 0); + } } cleanup.release (); @@ -1778,6 +1836,10 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base, return 0; prev_lm = 0; + + /* If the list is empty, we are seeing a new namespace for the + first time, so assign it an internal ID. */ + svr4_maybe_add_namespace (info, debug_base); } else prev_lm = solist.back ().lm_info->lm_addr; @@ -1845,6 +1907,8 @@ disable_probes_interface (svr4_info *info) free_probes_table (info); info->solib_lists.clear (); + info->namespace_id.clear (); + info->active_namespaces.clear (); } /* Update the solib list as appropriate when using the @@ -3042,6 +3106,8 @@ svr4_solib_create_inferior_hook (int from_tty) /* Clear the probes-based interface's state. */ free_probes_table (info); info->solib_lists.clear (); + info->namespace_id.clear (); + info->active_namespaces.clear (); /* Relocate the main executable if necessary. */ svr4_relocate_main_executable (); @@ -3361,6 +3427,32 @@ svr4_find_solib_addr (solib &so) return li->l_addr_inferior; } +/* See solib_ops::find_solib_ns in solist.h. */ + +static int +svr4_find_solib_ns (const solib &so) +{ + CORE_ADDR debug_base = find_debug_base_for_solib (&so); + svr4_info *info = get_svr4_info (current_program_space); + for (int i = 0; i < info->namespace_id.size (); i++) + { + if (info->namespace_id[i] == debug_base) + { + gdb_assert (info->active_namespaces.count (i) == 1); + return i; + } + } + error (_("No namespace found")); +} + +/* see solib_ops::num_active_namespaces in solist.h. */ +static int +svr4_num_active_namespaces () +{ + svr4_info *info = get_svr4_info (current_program_space); + return info->active_namespaces.size (); +} + const struct solib_ops svr4_so_ops = { svr4_relocate_section_addresses, @@ -3376,6 +3468,8 @@ const struct solib_ops svr4_so_ops = svr4_update_solib_event_breakpoints, svr4_handle_solib_event, svr4_find_solib_addr, + svr4_find_solib_ns, + svr4_num_active_namespaces, }; void _initialize_svr4_solib (); diff --git a/gdb/solib.c b/gdb/solib.c index 7782c8d699e..32dfa887ac7 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -1048,12 +1048,24 @@ info_sharedlibrary_command (const char *pattern, int from_tty) } } + /* How many columns the table should have. If the inferior has + more than one namespace active, we need a column to show that. */ + int num_cols = 4; + const solib_ops *ops = gdbarch_so_ops (gdbarch); + if (ops->num_active_namespaces != nullptr + && ops->num_active_namespaces () > 1) + num_cols++; + { - ui_out_emit_table table_emitter (uiout, 4, nr_libs, "SharedLibraryTable"); + ui_out_emit_table table_emitter (uiout, num_cols, nr_libs, + "SharedLibraryTable"); /* The "- 1" is because ui_out adds one space between columns. */ uiout->table_header (addr_width - 1, ui_left, "from", "From"); uiout->table_header (addr_width - 1, ui_left, "to", "To"); + if (ops->num_active_namespaces != nullptr + && ops->num_active_namespaces () > 1) + uiout->table_header (5, ui_left, "namespace", "NS"); uiout->table_header (12 - 1, ui_left, "syms-read", "Syms Read"); uiout->table_header (0, ui_noalign, "name", "Shared Object Library"); @@ -1080,6 +1092,19 @@ info_sharedlibrary_command (const char *pattern, int from_tty) uiout->field_skip ("to"); } + if (ops->num_active_namespaces != nullptr + && ops->num_active_namespaces ()> 1) + { + try + { + uiout->field_fmt ("namespace", "#%d", ops->find_solib_ns (so)); + } + catch (const gdb_exception_error &er) + { + uiout->field_skip ("namespace"); + } + } + if (!top_level_interpreter ()->interp_ui_out ()->is_mi_like_p () && so.symbols_loaded && !objfile_has_symbols (so.objfile)) { diff --git a/gdb/solist.h b/gdb/solist.h index 9a157a4bbae..03d2392b19d 100644 --- a/gdb/solist.h +++ b/gdb/solist.h @@ -180,6 +180,20 @@ struct solib_ops name). */ std::optional (*find_solib_addr) (solib &so); + + /* Return which linker namespace contains the current so. + If the linker or libc does not support linkage namespaces at all + (which is basically all of them but solib-svr4), this function should + be set to nullptr, so that "info shared" won't add an unnecessary + column. + + If the namespace can not be determined (such as when we're stepping + though the dynamic linker), this function should throw a + gdb_exception_error. */ + int (*find_solib_ns) (const solib &so); + + /* Returns the number of active namespaces in the inferior. */ + int (*num_active_namespaces) (); }; /* A unique pointer to a so_list. */ diff --git a/gdb/testsuite/gdb.base/attach-pie-noexec.exp b/gdb/testsuite/gdb.base/attach-pie-noexec.exp index 4e6ede12615..20c93b5599a 100644 --- a/gdb/testsuite/gdb.base/attach-pie-noexec.exp +++ b/gdb/testsuite/gdb.base/attach-pie-noexec.exp @@ -35,7 +35,7 @@ if ![runto_main] { } set test "sanity check info shared" gdb_test_multiple "info shared" $test { - -re "From\[ \t\]+To\[ \t\]+Syms Read\[ \t\]+Shared Object Library\r\n0x.*\r\n$gdb_prompt $" { + -re "From\[ \t\]+To(\\s+NS)?\[ \t\]+Syms Read\[ \t\]+Shared Object Library\r\n0x.*\r\n$gdb_prompt $" { pass $test } -re "No shared libraries loaded at this time\\.\r\n$gdb_prompt $" { @@ -62,6 +62,6 @@ if { ![gdb_attach $testpid] } { return } gdb_test "set architecture $arch" "The target architecture is set to \"$arch\"\\." -gdb_test "info shared" "From\[ \t\]+To\[ \t\]+Syms Read\[ \t\]+Shared Object Library\r\n0x.*" +gdb_test "info shared" "From\[ \t\]+To(\\s+NS)?\[ \t\]+Syms Read\[ \t\]+Shared Object Library\r\n0x.*" kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids-lib.c b/gdb/testsuite/gdb.base/dlmopen-ns-ids-lib.c new file mode 100644 index 00000000000..86cbb0f4e38 --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids-lib.c @@ -0,0 +1,28 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 . + +*/ + +int gdb_dlmopen_glob = 0; + +__attribute__((visibility ("default"))) +int +inc (int n) +{ + int amount = gdb_dlmopen_glob; + return n + amount; /* bp.inc. */ +} diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c b/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c new file mode 100644 index 00000000000..3bcd8196483 --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c @@ -0,0 +1,54 @@ +/* This testcase is part of GDB, the GNU debugger. + + 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 . + +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +int +main (void) +{ + void *handle[4]; + int (*fun) (int); + Lmid_t lmid; + int dl; + + handle[0] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[0] != NULL); + + handle[1] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[1] != NULL); + + handle[2] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); + assert (handle[2] != NULL); + + dlclose (handle[0]); /* TAG: first dlclose */ + dlclose (handle[1]); /* TAG: second dlclose */ + dlclose (handle[2]); /* TAG: third dlclose */ + + handle[3] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); + dlinfo (handle[3], RTLD_DI_LMID, &lmid); + + dlclose (handle[3]); /* TAG: fourth dlclose */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp new file mode 100644 index 00000000000..890ee091be9 --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -0,0 +1,108 @@ +# This testcase is part of GDB, the GNU debugger. +# +# 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 several things related to handling linker namespaces: +# * That the user-facing namespace ID is consistent; + +require allow_dlmopen_tests + +standard_testfile -main.c -lib.c + +set srcfile_lib $srcfile2 +set binfile_lib [standard_output_file dlmopen-lib.so] + +if { [build_executable "build shlib" $binfile_lib $srcfile_lib \ + [list debug shlib]] == -1 } { + return +} + +if { [build_executable "failed to build" $testfile $srcfile \ + [list additional_flags=-DDSO_NAME=\"$binfile_lib\" \ + shlib_load debug]] } { + return +} + +# Run the command "info sharedlibrary" and get the first namespace +# for the so +proc get_first_so_ns {} { + set ns -1 + gdb_test_multiple "info sharedlibrary" "get SO namespace" -lbl { + -re "From\\s+To\\s+\(NS\\s+\)?Syms\\s+Read\\s+Shared Object Library\r\n" { + exp_continue + } + -re "^$::hex\\s+$::hex\\s+\#($::decimal)\\s+\[^\r\n]+$::binfile_lib.*" { + set ns $expect_out(1,string) + } + -re "^$::gdb_prompt $" { + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } + } + return $ns +} + +# Run the tests relating to the command "info sharedlibrary", to +# verify that the namespace ID is consistent. +proc test_info_shared {} { + clean_restart $::binfile + + if { ![runto_main] } { + return + } + + # First test that we don't print a namespace column at the start. + gdb_test "info sharedlibrary" \ + "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library.*" \ + "before loading anything" + + gdb_test_no_output "set wait_for_gdb = 0" + + gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"] + gdb_continue_to_breakpoint "TAG: first dlclose" + + # Next, test that we *do* print a namespace column after loading SOs. + gdb_test "info sharedlibrary" \ + "From\\s+To\\s+NS\\s+Syms\\s+Read\\s+Shared Object Library.*" \ + "after loading everything" + + gdb_assert {[get_first_so_ns] == 1} "before closing any library" + + gdb_test "next" ".*second dlclose.*" "close first library" + gdb_assert {[get_first_so_ns] == 2} "after closing one library" + + gdb_test "next" ".*third dlclose.*" "close second library" + gdb_assert {[get_first_so_ns] == 3} "before closing two libraries" + + gdb_breakpoint [gdb_get_line_number "TAG: fourth dlclose"] + gdb_continue_to_breakpoint "TAG: fourth dlclose" + # As of writing this test, glibc's LMID is just an index on an array of + # namespaces. After closing a namespace, requesting a new one will + # return the index of the lowest-closed namespace, so this will likely + # be namespace 1, and because of glibc's reuse of the r_debug object, + # GDB should be able to assign the same number. + gdb_assert {[get_first_so_ns] == [get_integer_valueof "lmid" "-1"]} \ + "reopen a namespace" + + gdb_test "next" ".*return 0.*" "final namespace inactive" + gdb_test "info sharedlibrary" \ + "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library.*" \ + "after unloading everything" +} + +test_info_shared diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp index a8e3b08c016..6f8a3bc9de4 100644 --- a/gdb/testsuite/gdb.base/dlmopen.exp +++ b/gdb/testsuite/gdb.base/dlmopen.exp @@ -106,7 +106,7 @@ proc check_dso_count { dso num } { set count 0 gdb_test_multiple "info shared" "info shared" { - -re "$hex $hex Yes \[^\r\n\]*$dso\r\n" { + -re "$hex $hex \(\#$::decimal\\s+\)\?Yes \[^\r\n\]*$dso\r\n" { # use longer form so debug remote does not interfere set count [expr $count + 1] exp_continue @@ -233,12 +233,12 @@ proc get_dyld_info {} { set dyld_count 0 set dyld_start_addr "" gdb_test_multiple "info sharedlibrary" "" { - -re "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library\r\n" { + -re "From\\s+To\\s+\(NS\\s+\)?Syms\\s+Read\\s+Shared Object Library\r\n" { exp_continue } - -re "^($::hex)\\s+$::hex\\s+\[^/\]+(/\[^\r\n\]+)\r\n" { + -re "^($::hex)\\s+$::hex\\s+\(\#$::decimal\\s+\)?\[^/\]+(/\[^\r\n\]+)\r\n" { set addr $expect_out(1,string) - set lib $expect_out(2,string) + set lib $expect_out(3,string) if { [is_dyln $lib] } { # This looks like it might be the dynamic linker. diff --git a/gdb/testsuite/gdb.mi/mi-dlmopen.exp b/gdb/testsuite/gdb.mi/mi-dlmopen.exp index a5743f83bb8..c0208ebcc51 100644 --- a/gdb/testsuite/gdb.mi/mi-dlmopen.exp +++ b/gdb/testsuite/gdb.mi/mi-dlmopen.exp @@ -81,12 +81,12 @@ proc get_dyld_info {} { set dyld_count 0 set dyld_start_addr "" gdb_test_multiple "info sharedlibrary" "" { - -re "~\"From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library\\\\n\"\r\n" { + -re "~\"From\\s+To(\\s+NS)?\\s+Syms\\s+Read\\s+Shared Object Library\\\\n\"\r\n" { exp_continue } - -re "^~\"($::hex)\\s+$::hex\\s+\[^/\]+(/\[^\r\n\]+)\\\\n\"\r\n" { + -re "^~\"($::hex)\\s+${::hex}(\\s+$::decimal)?\\s+\[^/\]+(/\[^\r\n\]+)\\\\n\"\r\n" { set addr $expect_out(1,string) - set lib $expect_out(2,string) + set lib $expect_out(3,string) if { [is_dyln $lib] } { # This looks like it might be the dynamic linker. base-commit: 82455bdab80f9a4d4f94a6e8590a1bdc58c4010e -- 2.48.1