This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [patch] Fix dangling displays in separate debug
On Sun, 11 Apr 2010 03:27:35 +0200, Pedro Alves wrote:
> On Friday 09 April 2010 21:00:46, Jan Kratochvil wrote:
> > I was convinced due to some invalid reasons solib->objfile cannot be NULL.
>
> There's an easy way to see that state:
>
> (gdb) nosharedlibrary
> (gdb) info sharedlibrary
Nice reproducer, thanks.
> Doesn't this make it so that only the separate debug info
> case is exercised, and, it stops exercising the non-separate
> debug info case? I think we should instead be running the
> relevant parts the test twice instead?
I was asking about it before. Therefore reworked it as promised.
On Sat, 03 Apr 2010 11:55:58 +0200, Jan Kratochvil wrote:
# One may only address that gdb.base/solib-display.exp was testing symbol
# in-objfile while now it tests only symbol in-sepdebug-objfile and no longer
# the in-objfile case. I find the in-sepdebug-objfile as a superset of
# in-objfile test but I can rework it if anyones addresses this test change.
> > +if {[gdb_gnu_strip_debug $binfile_lib] != 0} {
> > + fail $test
>
> Not all systems support this, so this should be `unsupported' instead.
You are right, found at least one such target - i386-msdos:
+ ./binutils/objcopy --add-gnu-debuglink=1.o-debug 1.o-stripped 1.o-dest
BFD: 1.o-dest: can not represent section `.gnu_debuglink' in a.out object file format
./binutils/objcopy:1.o-dest: cannot fill debug link section `1.o-debug': Nonrepresentable section on output
With the extended requirements I rather pushed the former patch posted at:
Re: [patch 3/8] Types GC [display_uses_solib_p to exp_iterate]
http://sourceware.org/ml/gdb-patches/2009-07/msg00054.html
There was forgotten handling of TYPE_INSTANCE, UNOP_DYNAMIC_CAST and
UNOP_REINTERPRET_CAST. Reduced it only for the current applicable use case;
to be extended one day for the types garbage collector on its resubmit.
No regressions on {x86_64,x86_64-m32,i686}-fedora12-linux-gnu.
Thanks,
Jan
gdb/
2010-04-19 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix crashes on dangling display expressions.
* ada-lang.c (ada_operator_check): New function.
(ada_exp_descriptor): Fill-in the field operator_check.
* c-lang.c (exp_descriptor_c): Fill-in the field operator_check.
* jv-lang.c (exp_descriptor_java): Likewise.
* m2-lang.c (exp_descriptor_modula2): Likewise.
* scm-lang.c (exp_descriptor_scm): Likewise.
* parse.c (exp_descriptor_standard): Likewise.
(operator_check_standard): New function.
(exp_iterate, exp_uses_objfile_iter, exp_uses_objfile): New functions.
* parser-defs.h (struct exp_descriptor): New field operator_check.
(operator_check_standard, exp_uses_objfile): New declarations.
* printcmd.c: Remove the inclusion of solib.h.
(display_uses_solib_p): Remove the function.
(clear_dangling_display_expressions): Call lookup_objfile_from_block
and exp_uses_objfile instead of display_uses_solib_p.
* solist.h (struct so_list) <objfile>: New comment.
* symtab.c (lookup_objfile_from_block): Remove the static qualifier.
* symtab.h (lookup_objfile_from_block): New declaration.
(struct general_symbol_info) <obj_section>: Extend the comment.
gdb/testsuite/
2010-04-19 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix crashes on dangling display expressions.
* gdb.base/solib-display.exp: Call gdb_gnu_strip_debug if LIBSEPDEBUG
is SEP.
(lib_flags): Remove the "debug" keyword.
(libsepdebug): New variable for iterating new loop.
(save_pf_prefix): New variable wrapping the loop.
(sep_lib_flags): New variable derived from LIB_FLAGS. Use it.
* lib/gdb.exp (gdb_gnu_strip_debug): Document the return code.
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10868,6 +10868,36 @@ ada_operator_length (struct expression *exp, int pc, int *oplenp, int *argsp)
}
}
+/* Implementation of the exp_descriptor method operator_check. */
+
+static int
+ada_operator_check (struct expression *exp, int pos,
+ int (*objfile_func) (struct objfile *objfile, void *data),
+ void *data)
+{
+ const union exp_element *const elts = exp->elts;
+ struct type *type = NULL;
+
+ switch (elts[pos].opcode)
+ {
+ case UNOP_IN_RANGE:
+ case UNOP_QUAL:
+ type = elts[pos + 1].type;
+ break;
+
+ default:
+ return operator_check_standard (exp, pos, objfile_func, data);
+ }
+
+ /* Invoke callbacks for TYPE and OBJFILE if they were set as non-NULL. */
+
+ if (type && TYPE_OBJFILE (type)
+ && (*objfile_func) (TYPE_OBJFILE (type), data))
+ return 1;
+
+ return 0;
+}
+
static char *
ada_op_name (enum exp_opcode opcode)
{
@@ -11256,6 +11286,7 @@ parse (void)
static const struct exp_descriptor ada_exp_descriptor = {
ada_print_subexp,
ada_operator_length,
+ ada_operator_check,
ada_op_name,
ada_dump_subexp_body,
ada_evaluate_subexp
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -1139,6 +1139,7 @@ static const struct exp_descriptor exp_descriptor_c =
{
print_subexp_standard,
operator_length_standard,
+ operator_check_standard,
op_name_standard,
dump_subexp_body_standard,
evaluate_subexp_c
--- a/gdb/jv-lang.c
+++ b/gdb/jv-lang.c
@@ -1162,6 +1162,7 @@ const struct exp_descriptor exp_descriptor_java =
{
print_subexp_standard,
operator_length_standard,
+ operator_check_standard,
op_name_standard,
dump_subexp_body_standard,
evaluate_subexp_java
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -356,6 +356,7 @@ const struct exp_descriptor exp_descriptor_modula2 =
{
print_subexp_standard,
operator_length_standard,
+ operator_check_standard,
op_name_standard,
dump_subexp_body_standard,
evaluate_subexp_modula2
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -62,6 +62,7 @@ const struct exp_descriptor exp_descriptor_standard =
{
print_subexp_standard,
operator_length_standard,
+ operator_check_standard,
op_name_standard,
dump_subexp_body_standard,
evaluate_subexp_standard
@@ -1373,6 +1374,150 @@ parser_fprintf (FILE *x, const char *y, ...)
va_end (args);
}
+/* Implementation of the exp_descriptor method operator_check. */
+
+int
+operator_check_standard (struct expression *exp, int pos,
+ int (*objfile_func) (struct objfile *objfile,
+ void *data),
+ void *data)
+{
+ const union exp_element *const elts = exp->elts;
+ struct type *type = NULL;
+ struct objfile *objfile = NULL;
+
+ /* Extended operators should have been already handled by exp_descriptor
+ iterate method of its specific language. */
+ gdb_assert (elts[pos].opcode < OP_EXTENDED0);
+
+ /* Track the callers of write_exp_elt_type for this table. */
+
+ switch (elts[pos].opcode)
+ {
+ case BINOP_VAL:
+ case OP_COMPLEX:
+ case OP_DECFLOAT:
+ case OP_DOUBLE:
+ case OP_LONG:
+ case OP_SCOPE:
+ case OP_TYPE:
+ case UNOP_CAST:
+ case UNOP_DYNAMIC_CAST:
+ case UNOP_REINTERPRET_CAST:
+ case UNOP_MAX:
+ case UNOP_MEMVAL:
+ case UNOP_MIN:
+ type = elts[pos + 1].type;
+ break;
+
+ case TYPE_INSTANCE:
+ {
+ LONGEST arg, nargs = elts[pos + 1].longconst;
+
+ for (arg = 0; arg < nargs; arg++)
+ {
+ struct type *type = elts[pos + 2 + arg].type;
+ struct objfile *objfile = TYPE_OBJFILE (type);
+
+ if (objfile && (*objfile_func) (objfile, data))
+ return 1;
+ }
+ }
+ break;
+
+ case UNOP_MEMVAL_TLS:
+ objfile = elts[pos + 1].objfile;
+ type = elts[pos + 2].type;
+ break;
+
+ case OP_VAR_VALUE:
+ {
+ const struct block *const block = elts[pos + 1].block;
+ const struct symbol *const symbol = elts[pos + 2].symbol;
+
+ /* Check objfile where the variable itself is placed.
+ SYMBOL_OBJ_SECTION (symbol) may be NULL. */
+ if ((*objfile_func) (SYMBOL_SYMTAB (symbol)->objfile, data))
+ return 1;
+
+ /* Check objfile where is placed the code touching the variable. */
+ objfile = lookup_objfile_from_block (block);
+
+ type = SYMBOL_TYPE (symbol);
+ }
+ break;
+ }
+
+ /* Invoke callbacks for TYPE and OBJFILE if they were set as non-NULL. */
+
+ if (type && TYPE_OBJFILE (type)
+ && (*objfile_func) (TYPE_OBJFILE (type), data))
+ return 1;
+ if (objfile && (*objfile_func) (objfile, data))
+ return 1;
+
+ return 0;
+}
+
+/* Call OBJFILE_FUNC for any TYPE and OBJFILE found being referenced by EXP.
+ The functions are never called with NULL OBJFILE. Functions get passed an
+ arbitrary caller supplied DATA pointer. If any of the functions returns
+ non-zero value then (any other) non-zero value is immediately returned to
+ the caller. Otherwise zero is returned after iterating through whole EXP.
+ */
+
+static int
+exp_iterate (struct expression *exp,
+ int (*objfile_func) (struct objfile *objfile, void *data),
+ void *data)
+{
+ int endpos;
+ const union exp_element *const elts = exp->elts;
+
+ for (endpos = exp->nelts; endpos > 0; )
+ {
+ int pos, args, oplen = 0;
+
+ exp->language_defn->la_exp_desc->operator_length (exp, endpos,
+ &oplen, &args);
+ gdb_assert (oplen > 0);
+
+ pos = endpos - oplen;
+ if (exp->language_defn->la_exp_desc->operator_check (exp, pos,
+ objfile_func, data))
+ return 1;
+
+ endpos = pos;
+ }
+
+ return 0;
+}
+
+/* Helper for exp_uses_objfile. */
+
+static int
+exp_uses_objfile_iter (struct objfile *exp_objfile, void *objfile_voidp)
+{
+ struct objfile *objfile = objfile_voidp;
+
+ if (exp_objfile->separate_debug_objfile_backlink)
+ exp_objfile = exp_objfile->separate_debug_objfile_backlink;
+
+ return exp_objfile == objfile;
+}
+
+/* Return 1 if EXP uses OBJFILE (and will become dangling when OBJFILE
+ is unloaded), otherwise return 0. OBJFILE must not be a separate debug info
+ file. */
+
+int
+exp_uses_objfile (struct expression *exp, struct objfile *objfile)
+{
+ gdb_assert (objfile->separate_debug_objfile_backlink == NULL);
+
+ return exp_iterate (exp, exp_uses_objfile_iter, objfile);
+}
+
void
_initialize_parse (void)
{
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -192,6 +192,11 @@ extern void operator_length (struct expression *, int, int *, int *);
extern void operator_length_standard (struct expression *, int, int *, int *);
+extern int operator_check_standard (struct expression *exp, int pos,
+ int (*objfile_func)
+ (struct objfile *objfile, void *data),
+ void *data);
+
extern char *op_name_standard (enum exp_opcode);
extern struct type *follow_types (struct type *);
@@ -270,6 +275,19 @@ struct exp_descriptor
the number of subexpressions it takes. */
void (*operator_length) (struct expression*, int, int*, int *);
+ /* Call TYPE_FUNC and OBJFILE_FUNC for any TYPE and OBJFILE found being
+ referenced by the single operator of EXP at position POS. Operator
+ parameters are located at positive (POS + number) offsets in EXP.
+ The functions should never be called with NULL TYPE or NULL OBJFILE.
+ Functions should get passed an arbitrary caller supplied DATA pointer.
+ If any of the functions returns non-zero value then (any other) non-zero
+ value should be immediately returned to the caller. Otherwise zero
+ should be returned. */
+ int (*operator_check) (struct expression *exp, int pos,
+ int (*objfile_func) (struct objfile *objfile,
+ void *data),
+ void *data);
+
/* Name of this operator for dumping purposes. */
char *(*op_name) (enum exp_opcode);
@@ -302,4 +320,6 @@ extern void print_subexp_standard (struct expression *, int *,
extern void parser_fprintf (FILE *, const char *, ...) ATTR_FORMAT (printf, 2 ,3);
+extern int exp_uses_objfile (struct expression *exp, struct objfile *objfile);
+
#endif /* PARSER_DEFS_H */
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -46,7 +46,6 @@
#include "exceptions.h"
#include "observer.h"
#include "solist.h"
-#include "solib.h"
#include "parser-defs.h"
#include "charset.h"
#include "arch-utils.h"
@@ -1859,51 +1858,6 @@ disable_display_command (char *args, int from_tty)
}
}
-/* Return 1 if D uses SOLIB (and will become dangling when SOLIB
- is unloaded), otherwise return 0. */
-
-static int
-display_uses_solib_p (const struct display *d,
- const struct so_list *solib)
-{
- int endpos;
- struct expression *const exp = d->exp;
- const union exp_element *const elts = exp->elts;
-
- if (d->block != NULL
- && d->pspace == solib->pspace
- && solib_contains_address_p (solib, d->block->startaddr))
- return 1;
-
- for (endpos = exp->nelts; endpos > 0; )
- {
- int i, args, oplen = 0;
-
- exp->language_defn->la_exp_desc->operator_length (exp, endpos,
- &oplen, &args);
- gdb_assert (oplen > 0);
-
- i = endpos - oplen;
- if (elts[i].opcode == OP_VAR_VALUE)
- {
- const struct block *const block = elts[i + 1].block;
- const struct symbol *const symbol = elts[i + 2].symbol;
-
- if (block != NULL
- && solib_contains_address_p (solib,
- block->startaddr))
- return 1;
-
- /* SYMBOL_OBJ_SECTION (symbol) may be NULL. */
- if (SYMBOL_SYMTAB (symbol)->objfile == solib->objfile)
- return 1;
- }
- endpos -= oplen;
- }
-
- return 0;
-}
-
/* display_chain items point to blocks and expressions. Some expressions in
turn may point to symbols.
Both symbols and blocks are obstack_alloc'd on objfile_stack, and are
@@ -1915,17 +1869,28 @@ display_uses_solib_p (const struct display *d,
static void
clear_dangling_display_expressions (struct so_list *solib)
{
+ struct objfile *objfile = solib->objfile;
struct display *d;
- struct objfile *objfile = NULL;
- for (d = display_chain; d; d = d->next)
+ /* With no symbol file we cannot have a block or expression from it. */
+ if (objfile == NULL)
+ return;
+ if (objfile->separate_debug_objfile_backlink)
+ objfile = objfile->separate_debug_objfile_backlink;
+ gdb_assert (objfile->pspace == solib->pspace);
+
+ for (d = display_chain; d != NULL; d = d->next)
{
- if (d->exp && display_uses_solib_p (d, solib))
- {
- xfree (d->exp);
- d->exp = NULL;
- d->block = NULL;
- }
+ if (d->pspace != solib->pspace)
+ continue;
+
+ if (lookup_objfile_from_block (d->block) == objfile
+ || (d->exp && exp_uses_objfile (d->exp, objfile)))
+ {
+ xfree (d->exp);
+ d->exp = NULL;
+ d->block = NULL;
+ }
}
}
--- a/gdb/scm-lang.c
+++ b/gdb/scm-lang.c
@@ -231,6 +231,7 @@ const struct exp_descriptor exp_descriptor_scm =
{
print_subexp_standard,
operator_length_standard,
+ operator_check_standard,
op_name_standard,
dump_subexp_body_standard,
evaluate_exp
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -63,7 +63,12 @@ struct so_list
bfd *abfd;
char symbols_loaded; /* flag: symbols read in yet? */
- struct objfile *objfile; /* objfile for loaded lib */
+
+ /* objfile with symbols for a loaded library. Target memory is read from
+ ABFD. OBJFILE may be NULL either before symbols have been loaded, if
+ the file cannot be found or after the command "nosharedlibrary". */
+ struct objfile *objfile;
+
struct target_section *sections;
struct target_section *sections_end;
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1150,7 +1150,7 @@ lookup_symbol_aux_local (const char *name, const struct block *block,
/* Look up OBJFILE to BLOCK. */
-static struct objfile *
+struct objfile *
lookup_objfile_from_block (const struct block *block)
{
struct objfile *obj;
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -148,7 +148,7 @@ struct general_symbol_info
short section;
- /* The section associated with this symbol. */
+ /* The section associated with this symbol. It can be NULL. */
struct obj_section *obj_section;
};
@@ -1206,4 +1206,6 @@ int producer_is_realview (const char *producer);
void fixup_section (struct general_symbol_info *ginfo,
CORE_ADDR addr, struct objfile *objfile);
+struct objfile *lookup_objfile_from_block (const struct block *block);
+
#endif /* !defined(SYMTAB_H) */
--- a/gdb/testsuite/gdb.base/solib-display.exp
+++ b/gdb/testsuite/gdb.base/solib-display.exp
@@ -36,7 +36,7 @@ if { [skip_shlib_tests] || [is_remote target] } {
set libname "solib-display-lib"
set srcfile_lib ${srcdir}/${subdir}/${libname}.c
set binfile_lib ${objdir}/${subdir}/${libname}.so
-set lib_flags [list debug]
+set lib_flags {}
# Binary file.
set testfile "solib-display-main"
set srcfile ${srcdir}/${subdir}/${testfile}.c
@@ -48,60 +48,92 @@ if [get_compiler_info ${binfile}] {
return -1
}
-if { [gdb_compile_shlib ${srcfile_lib} ${binfile_lib} $lib_flags] != ""
- || [gdb_compile ${srcfile} ${binfile} executable $bin_flags] != "" } {
- untested "Could not compile $binfile_lib or $binfile."
- return -1
+set save_pf_prefix $pf_prefix
+# SEP must be last for the possible `unsupported' error path.
+foreach libsepdebug {NO IN SEP} {
+
+ set pf_prefix $save_pf_prefix
+ lappend pf_prefix "$libsepdebug:"
+
+ set sep_lib_flags $lib_flags
+ if {$libsepdebug != "NO"} {
+ lappend sep_lib_flags {debug}
+ }
+ if { [gdb_compile_shlib ${srcfile_lib} ${binfile_lib} $sep_lib_flags] != ""
+ || [gdb_compile ${srcfile} ${binfile} executable $bin_flags] != "" } {
+ untested "Could not compile $binfile_lib or $binfile."
+ return -1
+ }
+
+ if {$libsepdebug == "SEP"} {
+ if {[gdb_gnu_strip_debug $binfile_lib] != 0} {
+ unsupported "Could not split debug of $binfile_lib."
+ return
+ } else {
+ pass "split solib"
+ }
+ }
+
+ clean_restart $executable
+
+ if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+ }
+
+ gdb_test "display a_global" "1: a_global = 41"
+ gdb_test "display b_global" "2: b_global = 42"
+ gdb_test "display c_global" "3: c_global = 43"
+
+ if { [gdb_start_cmd] < 0 } {
+ fail "Can't run to main (2)"
+ continue
+ }
+
+ gdb_test "" "3: c_global = 43\\r\\n2: b_global = 42\\r\\n1: a_global = 41" "after rerun"
+
+ # Now rebuild the library without b_global
+ if { [gdb_compile_shlib ${srcfile_lib} ${binfile_lib} \
+ "$sep_lib_flags additional_flags=-DNO_B_GLOBAL"] != ""} {
+ fail "Can't rebuild $binfile_lib"
+ }
+
+ if {$libsepdebug == "SEP"} {
+ set test "split solib second time"
+ if {[gdb_gnu_strip_debug $binfile_lib] != 0} {
+ fail $test
+ continue
+ } else {
+ pass $test
+ }
+ }
+
+ if { [gdb_start_cmd] < 0 } {
+ fail "Can't run to main (3)"
+ continue
+ }
+
+ gdb_test "" "3: c_global = 43\\r\\nwarning: .*b_global.*\\r\\n1: a_global = 41" "after rerun (2)"
+
+ # Now verify that displays which are not in the shared library
+ # are not cleared permaturely.
+
+ gdb_test "break [gdb_get_line_number "break here" ${testfile}.c]" \
+ ".*Breakpoint.* at .*"
+
+ gdb_test "continue"
+ gdb_test "display main_global" "4: main_global = 44"
+ gdb_test "display a_local" "5: a_local = 45"
+ gdb_test "display a_static" "6: a_static = 46"
+
+ if { [gdb_start_cmd] < 0 } {
+ fail "Can't run to main (4)"
+ continue
+ }
+
+ gdb_test "" "6: a_static = 46\\r\\n4: main_global = 44\\r\\n.*"
+ gdb_test "break [gdb_get_line_number "break here" ${testfile}.c]" \
+ ".*Breakpoint.* at .*"
+ gdb_test "continue" "6: a_static = 46\\r\\n5: a_local = 45\\r\\n4: main_global = 44\\r\\n.*"
}
-
-clean_restart ${executable}
-
-if ![runto_main] then {
- fail "Can't run to main"
- return 0
-}
-
-gdb_test "display a_global" "1: a_global = 41"
-gdb_test "display b_global" "2: b_global = 42"
-gdb_test "display c_global" "3: c_global = 43"
-
-if { [gdb_start_cmd] < 0 } {
- fail "Can't run to main (2)"
- return 0
-}
-
-gdb_test "" "3: c_global = 43\\r\\n2: b_global = 42\\r\\n1: a_global = 41" "after rerun"
-
-# Now rebuild the library without b_global
-if { [gdb_compile_shlib ${srcfile_lib} ${binfile_lib} \
- "$lib_flags additional_flags=-DNO_B_GLOBAL"] != ""} {
- fail "Can't rebuild $binfile_lib"
-}
-
-if { [gdb_start_cmd] < 0 } {
- fail "Can't run to main (3)"
- return 0
-}
-
-gdb_test "" "3: c_global = 43\\r\\nwarning: .*b_global.*\\r\\n1: a_global = 41" "after rerun"
-
-# Now verify that displays which are not in the shared library
-# are not cleared permaturely.
-
-gdb_test "break [gdb_get_line_number "break here" ${testfile}.c]" \
- ".*Breakpoint.* at .*"
-
-gdb_test "continue"
-gdb_test "display main_global" "4: main_global = 44"
-gdb_test "display a_local" "5: a_local = 45"
-gdb_test "display a_static" "6: a_static = 46"
-
-if { [gdb_start_cmd] < 0 } {
- fail "Can't run to main (4)"
- return 0
-}
-
-gdb_test "" "6: a_static = 46\\r\\n4: main_global = 44\\r\\n.*"
-gdb_test "break [gdb_get_line_number "break here" ${testfile}.c]" \
- ".*Breakpoint.* at .*"
-gdb_test "continue" "6: a_static = 46\\r\\n5: a_local = 45\\r\\n4: main_global = 44\\r\\n.*"
+set pf_prefix $save_pf_prefix
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -2896,6 +2896,9 @@ proc build_id_debug_filename_get { exec } {
# Create stripped files for DEST, replacing it. If ARGS is passed, it is a
# list of optional flags. The only currently supported flag is no-main,
# which removes the symbol entry for main from the separate debug file.
+#
+# Function returns zero on success. Function will return non-zero failure code
+# on some targets not supporting separate debug info (such as i386-msdos).
proc gdb_gnu_strip_debug { dest args } {