On 3/19/25 9:30 AM, Guinevere Larsen wrote: > 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. While doing some more development on the next patch for this series, it occurred to me that we also identify frames with #N, so this would be overlap of the meaning of #. This will eventually be somewhat confusing as we could possibly have the following: (gdb) bt #0  #1::roll () at random.c:6 #1  0x00000000004011b8 in #1::hit (chance=0, acc_boost=0, evasion_boost=0) at random.c:9 #2  0x000000000040126c in #0::main () at random.c:20 My question is: is this confusing enough that we should figure out a different syntax? and if so, what other invalid character should I use for a namespace? Also I have an unrelated note about my patch inlined. > > 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. > > --- > 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" This is leftover from an earlier version where I thought about using attach, but decided against it since dlmopen already tests it. I've removed this from my local branch -- Cheers, Guinevere Larsen She/Her/Hers > + > + 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