diff options
author | Pierre-Marie de Rodat <derodat@adacore.com> | 2015-02-05 17:00:06 +0100 |
---|---|---|
committer | Pierre-Marie de Rodat <derodat@adacore.com> | 2015-08-25 08:13:28 -0400 |
commit | 63e43d3aedb8b1112899c2d0ad74cbbee687e5d6 (patch) | |
tree | 9e35fea0ccd74ee5d2d27bc7fe957ec19c360dbb /gdb/findvar.c | |
parent | 7c2bea1ad1c779b893eb9cad4c92732583ae0eb5 (diff) | |
download | gdb-63e43d3aedb8b1112899c2d0ad74cbbee687e5d6.zip gdb-63e43d3aedb8b1112899c2d0ad74cbbee687e5d6.tar.gz gdb-63e43d3aedb8b1112899c2d0ad74cbbee687e5d6.tar.bz2 |
DWARF: handle non-local references in nested functions
GDB's current behavior when dealing with non-local references in the
context of nested fuctions is approximative:
- code using valops.c:value_of_variable read the first available stack
frame that holds the corresponding variable (whereas there can be
multiple candidates for this);
- code directly relying on read_var_value will instead read non-local
variables in frames where they are not even defined.
This change adds the necessary context to symbol reads (to get the block
they belong to) and to blocks (the static link property, if any) so that
GDB can make the proper decisions when dealing with non-local varibale
references.
gdb/ChangeLog:
* ada-lang.c (ada_read_var_value): Add a var_block argument
and pass it to default_read_var_value.
* block.c (block_static_link): New accessor.
* block.h (block_static_link): Declare it.
* buildsym.c (finish_block_internal): Add a static_link
argument. If there is a static link, associate it to the new
block.
(finish_block): Add a static link argument and pass it to
finish_block_internal.
(end_symtab_get_static_block): Update calls to finish_block and
to finish_block_internal.
(end_symtab_with_blockvector): Update call to
finish_block_internal.
* buildsym.h: Forward-declare struct dynamic_prop.
(struct context_stack): Add a static_link field.
(finish_block): Add a static link argument.
* c-exp.y: Remove an obsolete comment (evaluation of variables
already start from the selected frame, and now they climb *up*
the call stack) and propagate the block information to the
produced expression.
* d-exp.y: Likewise.
* f-exp.y: Likewise.
* go-exp.y: Likewise.
* jv-exp.y: Likewise.
* m2-exp.y: Likewise.
* p-exp.y: Likewise.
* coffread.c (coff_symtab_read): Update calls to finish_block.
* dbxread.c (process_one_symbol): Likewise.
* xcoffread.c (read_xcoff_symtab): Likewise.
* compile/compile-c-symbols.c (convert_one_symbol): Promote the
"sym" parameter to struct block_symbol, update its uses and pass
its block to calls to read_var_value.
(convert_symbol_sym): Update the calls to convert_one_symbol.
* compile/compile-loc2c.c (do_compile_dwarf_expr_to_c): Update
call to read_var_value.
* dwarf2loc.c (block_op_get_frame_base): New.
(dwarf2_block_frame_base_locexpr_funcs): Implement the
get_frame_base method.
(dwarf2_block_frame_base_loclist_funcs): Likewise.
(dwarf2locexpr_baton_eval): Add a frame argument and use it
instead of the selected frame in order to evaluate the
expression.
(dwarf2_evaluate_property): Add a frame argument. Update call
to dwarf2_locexpr_baton_eval to provide a frame in available and
to handle the absence of address stack.
* dwarf2loc.h (dwarf2_evaluate_property): Add a frame argument.
* dwarf2read.c (attr_to_dynamic_prop): Add a forward
declaration.
(read_func_scope): Record any available static link description.
Update call to finish_block.
(read_lexical_block_scope): Update call to finish_block.
* findvar.c (follow_static_link): New.
(get_hosting_frame): New.
(default_read_var_value): Add a var_block argument. Use
get_hosting_frame to handle non-local references.
(read_var_value): Add a var_block argument and pass it to the
LA_READ_VAR_VALUE method.
* gdbtypes.c (resolve_dynamic_range): Update calls to
dwarf2_evaluate_property.
(resolve_dynamic_type_internal): Likewise.
* guile/scm-frame.c (gdbscm_frame_read_var): Update call to
read_var_value, passing it the block coming from symbol lookup.
* guile/scm-symbol.c (gdbscm_symbol_value): Update call to
read_var_value (TODO).
* infcmd.c (finish_command_continuation): Update call to
read_var_value, passing it the block coming from symbol lookup.
* infrun.c (insert_exception_resume_breakpoint): Likewise.
* language.h (struct language_defn): Add a var_block argument to
the LA_READ_VAR_VALUE method.
* objfiles.c (struct static_link_htab_entry): New.
(static_link_htab_entry_hash): New.
(static_link_htab_entry_eq): New.
(objfile_register_static_link): New.
(objfile_lookup_static_link): New.
(free_objfile): Free the STATIC_LINKS hashed map if needed.
* objfiles.h: Include hashtab.h.
(struct objfile): Add a static_links field.
(objfile_register_static_link): New.
(objfile_lookup_static_link): New.
* printcmd.c (print_variable_and_value): Update call to
read_var_value.
* python/py-finishbreakpoint.c (bpfinishpy_init): Likewise.
* python/py-frame.c (frapy_read_var): Update call to
read_var_value, passing it the block coming from symbol lookup.
* python/py-framefilter.c (extract_sym): Add a sym_block
parameter and set the pointed value to NULL (TODO).
(enumerate_args): Update call to extract_sym.
(enumerate_locals): Update calls to extract_sym and to
read_var_value.
* python/py-symbol.c (sympy_value): Update call to
read_var_value (TODO).
* stack.c (read_frame_local): Update call to read_var_value.
(read_frame_arg): Likewise.
(return_command): Likewise.
* symtab.h (struct symbol_block_ops): Add a get_frame_base
method.
(struct symbol): Add a block field.
(SYMBOL_BLOCK): New accessor.
* valops.c (value_of_variable): Remove frame/block handling and
pass the block argument to read_var_value, which does this job
now.
(value_struct_elt_for_reference): Update calls to
read_var_value.
(value_of_this): Pass the block found to read_var_value.
* value.h (read_var_value): Add a var_block argument.
(default_read_var_value): Likewise.
gdb/testsuite/ChangeLog:
* gdb.base/nested-subp1.exp: New file.
* gdb.base/nested-subp1.c: New file.
* gdb.base/nested-subp2.exp: New file.
* gdb.base/nested-subp2.c: New file.
* gdb.base/nested-subp3.exp: New file.
* gdb.base/nested-subp3.c: New file.
Diffstat (limited to 'gdb/findvar.c')
-rw-r--r-- | gdb/findvar.c | 168 |
1 files changed, 164 insertions, 4 deletions
diff --git a/gdb/findvar.c b/gdb/findvar.c index 83b4fca..1c077f7 100644 --- a/gdb/findvar.c +++ b/gdb/findvar.c @@ -32,6 +32,7 @@ #include "block.h" #include "objfiles.h" #include "language.h" +#include "dwarf2loc.h" /* Basic byte-swapping routines. All 'extract' functions return a host-format integer from a target-format integer at ADDR which is @@ -409,11 +410,166 @@ minsym_lookup_iterator_cb (struct objfile *objfile, void *cb_data) return (data->result.minsym != NULL); } +/* Given static link expression and the frame it lives in, look for the frame + the static links points to and return it. Return NULL if we could not find + such a frame. */ + +static struct frame_info * +follow_static_link (struct frame_info *frame, + const struct dynamic_prop *static_link) +{ + CORE_ADDR upper_frame_base; + + if (!dwarf2_evaluate_property (static_link, frame, NULL, &upper_frame_base)) + return NULL; + + /* Now climb up the stack frame until we reach the frame we are interested + in. */ + for (; frame != NULL; frame = get_prev_frame (frame)) + { + struct symbol *framefunc = get_frame_function (frame); + + /* Stacks can be quite deep: give the user a chance to stop this. */ + QUIT; + + /* If we don't know how to compute FRAME's base address, don't give up: + maybe the frame we are looking for is upper in the stace frame. */ + if (framefunc != NULL + && SYMBOL_BLOCK_OPS (framefunc)->get_frame_base != NULL + && (SYMBOL_BLOCK_OPS (framefunc)->get_frame_base (framefunc, frame) + == upper_frame_base)) + break; + } + + return frame; +} + +/* Assuming VAR is a symbol that can be reached from FRAME thanks to lexical + rules, look for the frame that is actually hosting VAR and return it. If, + for some reason, we found no such frame, return NULL. + + This kind of computation is necessary to correctly handle lexically nested + functions. + + Note that in some cases, we know what scope VAR comes from but we cannot + reach the specific frame that hosts the instance of VAR we are looking for. + For backward compatibility purposes (with old compilers), we then look for + the first frame that can host it. */ + +static struct frame_info * +get_hosting_frame (struct symbol *var, const struct block *var_block, + struct frame_info *frame) +{ + const struct block *frame_block = NULL; + + if (!symbol_read_needs_frame (var)) + return NULL; + + /* Some symbols for local variables have no block: this happens when they are + not produced by a debug information reader, for instance when GDB creates + synthetic symbols. Without block information, we must assume they are + local to FRAME. In this case, there is nothing to do. */ + else if (var_block == NULL) + return frame; + + /* We currently assume that all symbols with a location list need a frame. + This is true in practice because selecting the location description + requires to compute the CFA, hence requires a frame. However we have + tests that embed global/static symbols with null location lists. + We want to get <optimized out> instead of <frame required> when evaluating + them so return a frame instead of raising an error. */ + else if (var_block == block_global_block (var_block) + || var_block == block_static_block (var_block)) + return frame; + + /* We have to handle the "my_func::my_local_var" notation. This requires us + to look for upper frames when we find no block for the current frame: here + and below, handle when frame_block == NULL. */ + if (frame != NULL) + frame_block = get_frame_block (frame, NULL); + + /* Climb up the call stack until reaching the frame we are looking for. */ + while (frame != NULL && frame_block != var_block) + { + /* Stacks can be quite deep: give the user a chance to stop this. */ + QUIT; + + if (frame_block == NULL) + { + frame = get_prev_frame (frame); + if (frame == NULL) + break; + frame_block = get_frame_block (frame, NULL); + } + + /* If we failed to find the proper frame, fallback to the heuristic + method below. */ + else if (frame_block == block_global_block (frame_block)) + { + frame = NULL; + break; + } + + /* Assuming we have a block for this frame: if we are at the function + level, the immediate upper lexical block is in an outer function: + follow the static link. */ + else if (BLOCK_FUNCTION (frame_block)) + { + const struct dynamic_prop *static_link + = block_static_link (frame_block); + int could_climb_up = 0; + + if (static_link != NULL) + { + frame = follow_static_link (frame, static_link); + if (frame != NULL) + { + frame_block = get_frame_block (frame, NULL); + could_climb_up = frame_block != NULL; + } + } + if (!could_climb_up) + { + frame = NULL; + break; + } + } + + else + /* We must be in some function nested lexical block. Just get the + outer block: both must share the same frame. */ + frame_block = BLOCK_SUPERBLOCK (frame_block); + } + + /* Old compilers may not provide a static link, or they may provide an + invalid one. For such cases, fallback on the old way to evaluate + non-local references: just climb up the call stack and pick the first + frame that contains the variable we are looking for. */ + if (frame == NULL) + { + frame = block_innermost_frame (var_block); + if (frame == NULL) + { + if (BLOCK_FUNCTION (var_block) + && !block_inlined_p (var_block) + && SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block))) + error (_("No frame is currently executing in block %s."), + SYMBOL_PRINT_NAME (BLOCK_FUNCTION (var_block))); + else + error (_("No frame is currently executing in specified" + " block")); + } + } + + return frame; +} + /* A default implementation for the "la_read_var_value" hook in the language vector which should work in most situations. */ struct value * -default_read_var_value (struct symbol *var, struct frame_info *frame) +default_read_var_value (struct symbol *var, const struct block *var_block, + struct frame_info *frame) { struct value *v; struct type *type = SYMBOL_TYPE (var); @@ -427,7 +583,10 @@ default_read_var_value (struct symbol *var, struct frame_info *frame) check_typedef (type); if (symbol_read_needs_frame (var)) - gdb_assert (frame); + gdb_assert (frame != NULL); + + if (frame != NULL) + frame = get_hosting_frame (var, var_block, frame); if (SYMBOL_COMPUTED_OPS (var) != NULL) return SYMBOL_COMPUTED_OPS (var)->read_variable (var, frame); @@ -610,14 +769,15 @@ default_read_var_value (struct symbol *var, struct frame_info *frame) /* Calls VAR's language la_read_var_value hook with the given arguments. */ struct value * -read_var_value (struct symbol *var, struct frame_info *frame) +read_var_value (struct symbol *var, const struct block *var_block, + struct frame_info *frame) { const struct language_defn *lang = language_def (SYMBOL_LANGUAGE (var)); gdb_assert (lang != NULL); gdb_assert (lang->la_read_var_value != NULL); - return lang->la_read_var_value (var, frame); + return lang->la_read_var_value (var, var_block, frame); } /* Install default attributes for register values. */ |