--- /dev/null
+/* Copyright 2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+struct bar
+{
+ int a;
+ int b;
+};
+
+struct bar global_shlib_var = { 2, 2 };
+
+void
+foo ()
+{
+ struct bar local_var = { 1, 1 };
+}
--- /dev/null
+/* Copyright 2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include <dlfcn.h>
+
+static void
+no_varobj_in_scope (void)
+{
+}
+
+static void
+floating_varobj_in_scope (void)
+{
+ int local_var = 0;
+}
+
+int
+main (int argc, char *argv [])
+{
+ void *shlib = dlopen (SHLIB_PATH, RTLD_LAZY);
+ void (*foo)() = dlsym (shlib, "foo");
+ foo ();
+ dlclose (shlib);
+
+ no_varobj_in_scope ();
+ floating_varobj_in_scope ();
+}
--- /dev/null
+# Copyright 2007-2022 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 that varobj are invalidated after the shlib they point to goes
+# away.
+
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+if { [skip_shlib_tests] } {
+ return 0
+}
+
+standard_testfile .c -lib.c
+set shlib_path [standard_output_file ${testfile}-lib.so]
+
+if { [gdb_compile_shlib $srcdir/$subdir/$srcfile2 $shlib_path {debug}] != "" } {
+ untested "failed to compile"
+ return -1
+}
+
+
+set opts [list shlib_load debug additional_flags=-DSHLIB_PATH="${shlib_path}"]
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $opts] != "" } {
+ untested "failed to compile"
+ return -1
+}
+
+proc do_test { separate_debuginfo } {
+ if { $separate_debuginfo } {
+ gdb_gnu_strip_debug $::shlib_path
+ }
+
+ if { [mi_clean_restart] } {
+ unsupported "failed to start GDB"
+ return
+ }
+
+ # Start the process once and create varobjs referencing the loaded objfiles.
+ with_test_prefix "setup" {
+ mi_load_shlibs $::shlib_path
+ if { $separate_debuginfo } {
+ mi_load_shlibs ${::shlib_path}.debug
+ }
+ mi_delete_breakpoints
+ mi_gdb_reinitialize_dir $::srcdir/$::subdir
+ mi_gdb_load $::binfile
+
+ mi_runto foo -pending
+
+ mi_create_varobj global_shlib_var global_shlib_var "create global gloal_shlib_var"
+ mi_create_floating_varobj floating_local local_var "create floating local_var"
+
+ # Advance to a point where the shlib's objfile have been deleted.
+ mi_continue_to "no_varobj_in_scope"
+ }
+
+ with_test_prefix "after objfile deleted" {
+ # The global shlib var was invalidated when the objfile got unloaded.
+ mi_gdb_test "-var-update global_shlib_var" \
+ "\\^done,changelist=\\\[\{name=\"global_shlib_var\",in_scope=\"invalid\",has_more=\"0\"\}\]" \
+ "global_shlib_var invalidated"
+
+ # The floating var is still valid but not in scope.
+ mi_gdb_test "-var-update floating_local" \
+ "\\^done,changelist=\\\[{name=\"floating_local\",in_scope=\"false\",type_changed=\"false\",has_more=\"0\"}\\\]" \
+ "floating_local still valid but not in scope"
+
+ # The varobj can be re-evaluated if the expression is valid in the current
+ # frame.
+ mi_continue_to "floating_varobj_in_scope"
+ mi_gdb_test "-var-update floating_local" \
+ "\\^done,changelist=\\\[{name=\"floating_local\",in_scope=\"true\",type_changed=\"true\",new_type=\"int\",new_num_children=\"0\",has_more=\"0\"}\\\]" \
+ "floating_local in scope with new type and value"
+ }
+}
+
+foreach_with_prefix separate_debuginfo {0 1} {
+ do_test $separate_debuginfo
+}
#include "cli/cli-style.h"
#include "expop.h"
#include "inferior.h"
+#include "varobj.h"
/* Definition of a user function. */
struct internal_function
}
}
+/* Make sure that all types and values referenced by VAROBJ are updated before
+ OBJFILE is discarded. COPIED_TYPES is used to prevent cycles and
+ duplicates. */
+
+static void
+preserve_one_varobj (struct varobj *varobj, struct objfile *objfile,
+ htab_t copied_types)
+{
+ if (varobj->type->is_objfile_owned ()
+ && varobj->type->objfile_owner () == objfile)
+ {
+ varobj->type
+ = copy_type_recursive (objfile, varobj->type, copied_types);
+ }
+
+ if (varobj->value != nullptr)
+ preserve_one_value (varobj->value.get (), objfile, copied_types);
+}
+
/* Update the internal variables and value history when OBJFILE is
discarded; we must copy the types out of the objfile. New global types
will be created for every convenience variable which currently points to
for (var = internalvars; var; var = var->next)
preserve_one_internalvar (var, objfile, copied_types.get ());
+ /* For the remaining varobj, check that none has type owned by OBJFILE. */
+ all_root_varobjs ([&copied_types, objfile] (struct varobj *varobj)
+ {
+ preserve_one_varobj (varobj, objfile,
+ copied_types.get ());
+ });
+
preserve_ext_lang_values (objfile, copied_types.get ());
}
#include "parser-defs.h"
#include "gdbarch.h"
#include <algorithm>
+#include "observable.h"
#if HAVE_PYTHON
#include "python/python.h"
/* The expression for this parent. */
expression_up exp;
+ /* Cached arch from exp, for use in case exp gets invalidated. */
+ struct gdbarch *gdbarch = nullptr;
+
+ /* Cached language from exp, for use in case exp gets invalidated. */
+ const struct language_defn *language_defn = nullptr;
+
/* Block for which this expression is valid. */
const struct block *valid_block = NULL;
/* See python-internal.h. */
gdbpy_enter_varobj::gdbpy_enter_varobj (const struct varobj *var)
-: gdbpy_enter (var->root->exp->gdbarch, var->root->exp->language_defn)
+: gdbpy_enter (var->root->gdbarch, var->root->language_defn)
{
}
try
{
var->root->exp = parse_exp_1 (&p, pc, block, 0, &tracker);
+
+ /* Cache gdbarch and language_defn as they might be used even
+ after var is invalidated and var->root->exp cleared. */
+ var->root->gdbarch = var->root->exp->gdbarch;
+ var->root->language_defn = var->root->exp->language_defn;
}
catch (const gdb_exception_error &except)
all_root_varobjs (varobj_invalidate_iter);
}
+/* Ensure that no varobj keep references to OBJFILE. */
+
+static void
+varobj_invalidate_if_uses_objfile (struct objfile *objfile)
+{
+ if (objfile->separate_debug_objfile_backlink != nullptr)
+ objfile = objfile->separate_debug_objfile_backlink;
+
+ all_root_varobjs ([objfile] (struct varobj *var)
+ {
+ if (var->root->valid_block != nullptr)
+ {
+ struct objfile *bl_objfile = block_objfile (var->root->valid_block);
+ if (bl_objfile->separate_debug_objfile_backlink != nullptr)
+ bl_objfile = bl_objfile->separate_debug_objfile_backlink;
+
+ if (bl_objfile == objfile)
+ {
+ /* The varobj is tied to a block which is going away. There is
+ no way to reconstruct something later, so invalidate the
+ varobj completly and drop the reference to the block which is
+ being freed. */
+ var->root->is_valid = false;
+ var->root->valid_block = nullptr;
+ }
+ }
+
+ if (var->root->exp != nullptr
+ && exp_uses_objfile (var->root->exp.get (), objfile))
+ {
+ /* The varobj's current expression references the objfile. For
+ globals and floating, it is possible that when we try to
+ re-evaluate the expression later it is still valid with
+ whatever is in scope at that moment. Just invalidate the
+ expression for now. */
+ var->root->exp.reset ();
+
+ /* It only makes sense to keep a floating varobj around. */
+ if (!var->root->floating)
+ var->root->is_valid = false;
+ }
+
+ /* var->value->type and var->type might also reference the objfile.
+ This is taken care of in value.c:preserve_values which deals with
+ making sure that objfile-owend types are replaced with
+ gdbarch-owned equivalents. */
+ });
+}
+
/* A hash function for a varobj. */
static hashval_t
_("When non-zero, varobj debugging is enabled."),
NULL, show_varobjdebug,
&setdebuglist, &showdebuglist);
+
+ gdb::observers::free_objfile.attach (varobj_invalidate_if_uses_objfile,
+ "varobj");
}