diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/NEWS | 5 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 5 | ||||
-rw-r--r-- | gdb/solib-svr4.c | 108 | ||||
-rw-r--r-- | gdb/solib.c | 27 | ||||
-rw-r--r-- | gdb/solist.h | 14 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/attach-pie-noexec.exp | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen-ns-ids-lib.c | 28 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c | 54 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen-ns-ids.exp | 106 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/dlmopen.exp | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-dlmopen.exp | 6 |
11 files changed, 348 insertions, 17 deletions
@@ -35,6 +35,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 e034ac5..4e4509a 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -22172,6 +22172,11 @@ be determined then the address range for the @code{.text} section from the library will be listed. If the @code{.text} section cannot be found then no addresses will be listed. +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 398123f..2f839bd 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<CORE_ADDR, std::vector<svr4_so>> 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<CORE_ADDR> 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<size_t> active_namespaces; }; /* Per-program-space data key. */ static const registry<program_space>::key<svr4_info> 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 (); @@ -3460,6 +3526,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, @@ -3475,6 +3567,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 1d26970..3ddd4f9 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -1051,12 +1051,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"); @@ -1083,6 +1095,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 9a157a4..03d2392 100644 --- a/gdb/solist.h +++ b/gdb/solist.h @@ -180,6 +180,20 @@ struct solib_ops name). */ std::optional<CORE_ADDR> (*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 4e6ede1..20c93b5 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 0000000..86cbb0f --- /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 <http://www.gnu.org/licenses/>. + +*/ + +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 0000000..3bcd819 --- /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 <http://www.gnu.org/licenses/>. + +*/ + +#define _GNU_SOURCE +#include <dlfcn.h> +#include <stddef.h> +#include <assert.h> +#include <unistd.h> +#include <stdio.h> + +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 0000000..03b7a52 --- /dev/null +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -0,0 +1,106 @@ +# 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 <http://www.gnu.org/licenses/>. +# +# +# 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_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 a8e3b08..084c5bc 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 a5743f8..c0208eb 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. |