From: Sébastien Darche Date: Tue, 30 Sep 2025 18:54:30 +0000 (-0400) Subject: gdb: update shared libraries when inferior is created, even if no bfd exists X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1eb7027b3acd57676850cdb8b4900ec0c3ff40c;p=thirdparty%2Fbinutils-gdb.git gdb: update shared libraries when inferior is created, even if no bfd exists I noticed an unexpected behaviour difference when loading a core file in GDB depending on whether the main executable can be accessed or not. If GDB knows about the main executable, then symbols for unrelated libraries (such as libc) are loaded. If GDB cannot find the main executable, then they are not. Here is a reproducer using the artifacts from gdb.python/py-missing-objfile.exp. This test is ideal to reproduce the problem, because it hides from GDB the executable and library used to generate the core file. In the "good" case, where we give an executable to GDB (in addition to the core), we get a complete backtrace (assuming GDB can find debug symbols for glibc): $ ./gdb -nx -q --data-directory=data-directory testsuite/outputs/gdb.python/py-missing-objfile/hidden/py-missing-objfile -c testsuite/outputs/gdb.python/py-missing-objfile/py-missing-objfile.core -ex bt -batch ... #0 __pthread_kill_implementation (threadid=, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44 #1 0x00007fcbd0c98a13 in __pthread_kill_internal (threadid=, signo=6) at pthread_kill.c:89 #2 0x00007fcbd0c3e410 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26 #3 0x00007fcbd0c2557a in __GI_abort () at abort.c:77 #4 0x000055561659d152 in dump_core () at /home/sdarche/binutils-gdb/gdb/testsuite/gdb.python/py-missing-objfile.c:34 #5 0x000055561659d182 in main () at /home/sdarche/binutils-gdb/gdb/testsuite/gdb.python/py-missing-objfile.c:46 Or, if debug symbols for glibc aren't available, GDB at least tells us from which .so each frame comes from: #0 0x00007fcbd0c9894c in ?? () from /usr/lib/libc.so.6 #1 0x00007fcbd0c3e410 in raise () from /usr/lib/libc.so.6 #2 0x00007fcbd0c2557a in abort () from /usr/lib/libc.so.6 #3 0x000055561659d152 in dump_core () at /home/sdarche/binutils-gdb/gdb/testsuite/gdb.python/py-missing-objfile.c:34 #4 0x000055561659d182 in main () at /home/sdarche/binutils-gdb/gdb/testsuite/gdb.python/py-missing-objfile.c:46 If we omit passing the main executable to GDB (and GDB has no way to find it, because the test moved it on purpose), we get: $ ./gdb -nx -q --data-directory=data-directory -c testsuite/outputs/gdb.python/py-missing-objfile/py-missing-objfile.core -ex bt -batch #0 0x00007fcbd0c9894c in ?? () #1 0x0000000000000000 in ?? () Upon investigating, the "normal" path to load associated sos is through the post_create_inferior function in infcmd.c, which calls solib_create_inferior_hook. The solib_ops in turn loads the symbols by calling solib_add. When loading a core file, the list of loaded shared libraries can be found (and as we can see with `info sharedlibraries`, this is done properly). However, the current control flow handling in post_create_inferior does not ask the solib to load the symbols through solib_create_inferior_hook if a main exec_bfd is not present. The proposed change eliminates the if statement in the post_create_inferior function. This change may have side-effects on some solib_ops which may not check if the current inferior has a valid exec_bfd(). This commit also modifies the gdb.python/py-missing-objfile.exp test, providing a test case in which the main exec file is missing, but not the shared library. This is a good way to confirm this fix works, as the symbols from the shared library should be found even though the main exec file is missing. The test cases also ask GDB to clean_restart to ensure there is no leftover bfd or symbols. Furthermore, the total number of calls to the missing objfiles handler is 4 in the "all objfiles missing" test case instead of the previous 3 : - 2 for the mapped files (exec and so), in core_target_build_file_mappings - 1 for the core file exec, in locate_exec_from_corefile_build_id - 1 for the missing so, in solib_map_sections The changes in check_loaded_debug handle the case where no symbol exist at all, and also when a symbol table exists (but the symbol cannot be found) Note: Some comments in some solib_ops (frv, dbst) seem to indicate that solib_add should be called before solib_create_inferior_hook by post_create_inferior, but this does not seem to be the case anymore. Those comments might need to be updated. Change-Id: I517ff85813c941215b19fa8540c77f755a0aca28 Reviewed-By: Tankut Baris Aktemur Tested-By: Guinevere Larsen Approved-By: Simon Marchi --- diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 862cef6c1bb..0b9ca48b70f 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -402,36 +402,35 @@ post_create_inferior (int from_tty, bool set_pspace_solib_ops) (gdbarch_make_solib_ops (current_inferior ()->arch (), current_program_space)); - if (current_program_space->exec_bfd ()) - { - const unsigned solib_add_generation - = current_program_space->solib_add_generation; + { + const unsigned solib_add_generation + = current_program_space->solib_add_generation; - scoped_restore restore_in_initial_library_scan - = make_scoped_restore (¤t_inferior ()->in_initial_library_scan, - true); + scoped_restore restore_in_initial_library_scan + = make_scoped_restore (¤t_inferior ()->in_initial_library_scan, + true); - /* Create the hooks to handle shared library load and unload - events. */ - solib_create_inferior_hook (from_tty); + /* Create the hooks to handle shared library load and unload + events. */ + solib_create_inferior_hook (from_tty); - if (current_program_space->solib_add_generation == solib_add_generation) - { - /* The platform-specific hook should load initial shared libraries, - but didn't. FROM_TTY will be incorrectly 0 but such solib - targets should be fixed anyway. Call it only after the solib - target has been initialized by solib_create_inferior_hook. */ - - if (info_verbose) - warning (_("platform-specific solib_create_inferior_hook did " - "not load initial shared libraries.")); - - /* If the solist is global across processes, there's no need to - refetch it here. */ - if (!gdbarch_has_global_solist (current_inferior ()->arch ())) - solib_add (nullptr, 0, auto_solib_add); - } - } + if (current_program_space->solib_add_generation == solib_add_generation) + { + /* The platform-specific hook should load initial shared libraries, + but didn't. FROM_TTY will be incorrectly 0 but such solib + targets should be fixed anyway. Call it only after the solib + target has been initialized by solib_create_inferior_hook. */ + + if (info_verbose) + warning (_("platform-specific solib_create_inferior_hook did " + "not load initial shared libraries.")); + + /* If the solist is global across processes, there's no need to + refetch it here. */ + if (!gdbarch_has_global_solist (current_inferior ()->arch ())) + solib_add (nullptr, 0, auto_solib_add); + } + } /* If the user sets watchpoints before execution having started, then she gets software watchpoints, because GDB can't know which diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.exp b/gdb/testsuite/gdb.python/py-missing-objfile.exp index bb747d69a1b..e5e086130b0 100644 --- a/gdb/testsuite/gdb.python/py-missing-objfile.exp +++ b/gdb/testsuite/gdb.python/py-missing-objfile.exp @@ -96,18 +96,34 @@ proc check_loaded_debug { exec_loaded lib_loaded } { if { $exec_loaded } { gdb_test "whatis global_exec_var" "^type = volatile struct exec_type" - - if { $lib_loaded } { - gdb_test "whatis global_lib_var" "^type = volatile struct lib_type" - } else { - gdb_test "whatis global_lib_var" \ - "^No symbol \"global_lib_var\" in current context\\." + } else { + # If the debug info for libc, etc. are available, there might + # be a symbol table. + gdb_test_multiple "whatis global_exec_var" "" { + -re -wrap "No symbol \"global_exec_var\" in current context\\." { + pass $gdb_test_name + } + + -re -wrap "No symbol table is loaded\\. Use the \"file\" command\\." { + pass $gdb_test_name + } } + } + + if { $lib_loaded } { + gdb_test "whatis global_lib_var" "^type = volatile struct lib_type" } else { - gdb_test "whatis global_exec_var" \ - "^No symbol table is loaded\\. Use the \"file\" command\\." - gdb_test "whatis global_lib_var" \ - "^No symbol table is loaded\\. Use the \"file\" command\\." + # If the debug info for libc, etc. are available, there might + # be a symbol table. + gdb_test_multiple "whatis global_lib_var" "" { + -re -wrap "No symbol \"global_lib_var\" in current context\\." { + pass $gdb_test_name + } + + -re -wrap "No symbol table is loaded\\. Use the \"file\" command\\." { + pass $gdb_test_name + } + } } } @@ -173,6 +189,8 @@ with_test_prefix "no objfiles, no debug-file-directory" { # Setup some debug-file-directories. set debugdir_no_lib \ [setup_debugdir "debugdir.no-lib" [list "$hidden_binfile"]] +set debugdir_no_main \ + [setup_debugdir "debugdir.no-main" [list "$hidden_libfile"]] set debugdir_empty \ [setup_debugdir "debugdir.empty" {}] set debugdir_all \ @@ -196,6 +214,7 @@ require {expr {$expect_build_id_in_core_file_libfile}} with_test_prefix "all objfiles available" { # Another sanity check that GDB can find the files via the # debug-file-directory. + clean_restart set_debug_file_dir $debugdir_all load_core_file check_loaded_debug true true @@ -204,11 +223,21 @@ with_test_prefix "all objfiles available" { with_test_prefix "lib objfile missing" { # Another sanity check that GDB can find the files via the # debug-file-directory. + clean_restart set_debug_file_dir $debugdir_no_lib load_core_file check_loaded_debug true false } +with_test_prefix "main objfile missing" { + # Another sanity check that GDB can find the files via the + # debug-file-directory. + clean_restart + set_debug_file_dir $debugdir_no_main + load_core_file + check_loaded_debug false true +} + with_test_prefix "all objfiles missing, handler returns None" { clean_restart_load_python gdb_test_no_output \ @@ -218,11 +247,11 @@ with_test_prefix "all objfiles missing, handler returns None" { check_loaded_debug false false - # The handler should be called three times, once for the - # mapped-file, once for the core-file's exec, and once for the + # The handler should be called four times, twice for the + # mapped-files, once for the core-file's exec, and once for the # shared library. - gdb_test "python print(handler_obj.call_count)" "^3" \ - "check handler was called three times" + gdb_test "python print(handler_obj.call_count)" "^4" \ + "check handler was called four times" } with_test_prefix "lib objfile missing, handler returns None" {