diff options
-rw-r--r-- | gdb/ChangeLog | 24 | ||||
-rw-r--r-- | gdb/dwarf2read.c | 58 | ||||
-rw-r--r-- | gdb/objfiles.h | 5 | ||||
-rw-r--r-- | gdb/psymtab.c | 82 | ||||
-rw-r--r-- | gdb/rust-lang.c | 53 | ||||
-rw-r--r-- | gdb/symfile-debug.c | 28 | ||||
-rw-r--r-- | gdb/symfile.h | 8 | ||||
-rw-r--r-- | gdb/symtab.c | 39 | ||||
-rw-r--r-- | gdb/symtab.h | 19 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.rust/traits.exp | 47 | ||||
-rw-r--r-- | gdb/testsuite/gdb.rust/traits.rs | 37 |
12 files changed, 405 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7d58046..b7ee869 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,27 @@ +2017-11-17 Tom Tromey <tom@tromey.com> + + * symtab.h (struct symbol) <is_rust_vtable>: New member. + (struct rust_vtable_symbol): New. + (find_symbol_at_address): Declare. + * symtab.c (find_symbol_at_address): New function. + * symfile.h (struct quick_symbol_functions) + <find_compunit_symtab_by_address>: New member. + * symfile-debug.c (debug_qf_find_compunit_symtab_by_address): New + function. + (debug_sym_quick_functions): Link to + debug_qf_find_compunit_symtab_by_address. + * rust-lang.c (rust_get_trait_object_pointer): New function. + (rust_evaluate_subexp) <case UNOP_IND>: New case. Call + rust_get_trait_object_pointer. + * psymtab.c (psym_relocate): Clear psymbol_map. + (psym_fill_psymbol_map, psym_find_compunit_symtab_by_address): New + functions. + (psym_functions): Link to psym_find_compunit_symtab_by_address. + * objfiles.h (struct objfile) <psymbol_map>: New member. + * dwarf2read.c (dwarf2_gdb_index_functions): Update. + (process_die) <DW_TAG_variable>: New case. Call read_variable. + (rust_containing_type, read_variable): New functions. + 2017-11-17 Simon Marchi <simon.marchi@polymtl.ca> * common/gdb_vecs.h (DEF_VEC_I (int)): Remove. diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index b6bd291..8283345 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -1797,6 +1797,8 @@ static void read_lexical_block_scope (struct die_info *, struct dwarf2_cu *); static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu); +static void read_variable (struct die_info *die, struct dwarf2_cu *cu); + static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *, struct dwarf2_cu *, struct partial_symtab *); @@ -4995,6 +4997,7 @@ const struct quick_symbol_functions dwarf2_gdb_index_functions = dw2_map_matching_symbols, dw2_expand_symtabs_matching, dw2_find_pc_sect_compunit_symtab, + NULL, dw2_map_symbol_filenames }; @@ -9230,6 +9233,10 @@ process_die (struct die_info *die, struct dwarf2_cu *cu) process_imported_unit_die (die, cu); break; + case DW_TAG_variable: + read_variable (die, cu); + break; + default: new_symbol (die, NULL, cu); break; @@ -12770,6 +12777,57 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) } } +/* Helper function for read_variable. If DIE represents a virtual + table, then return the type of the concrete object that is + associated with the virtual table. Otherwise, return NULL. */ + +static struct type * +rust_containing_type (struct die_info *die, struct dwarf2_cu *cu) +{ + struct attribute *attr = dwarf2_attr (die, DW_AT_type, cu); + if (attr == NULL) + return NULL; + + /* Find the type DIE. */ + struct die_info *type_die = NULL; + struct dwarf2_cu *type_cu = cu; + + if (attr_form_is_ref (attr)) + type_die = follow_die_ref (die, attr, &type_cu); + if (type_die == NULL) + return NULL; + + if (dwarf2_attr (type_die, DW_AT_containing_type, type_cu) == NULL) + return NULL; + return die_containing_type (type_die, type_cu); +} + +/* Read a variable (DW_TAG_variable) DIE and create a new symbol. */ + +static void +read_variable (struct die_info *die, struct dwarf2_cu *cu) +{ + struct rust_vtable_symbol *storage = NULL; + + if (cu->language == language_rust) + { + struct type *containing_type = rust_containing_type (die, cu); + + if (containing_type != NULL) + { + struct objfile *objfile = cu->objfile; + + storage = OBSTACK_ZALLOC (&objfile->objfile_obstack, + struct rust_vtable_symbol); + initialize_objfile_symbol (storage); + storage->concrete_type = containing_type; + storage->is_rust_vtable = 1; + } + } + + new_symbol_full (die, NULL, cu, storage); +} + /* Call CALLBACK from DW_AT_ranges attribute value OFFSET reading .debug_rnglists. Callback's type should be: diff --git a/gdb/objfiles.h b/gdb/objfiles.h index 12ef5ec..62bd454 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -361,6 +361,11 @@ struct objfile struct psymbol_bcache *psymbol_cache; + /* Map symbol addresses to the partial symtab that defines the + object at that address. */ + + std::vector<std::pair<CORE_ADDR, partial_symtab *>> psymbol_map; + /* Vectors of all partial symbols read in from file. The actual data is stored in the objfile_obstack. */ diff --git a/gdb/psymtab.c b/gdb/psymtab.c index 06a65bf..c74ea21 100644 --- a/gdb/psymtab.c +++ b/gdb/psymtab.c @@ -36,6 +36,7 @@ #include "cp-support.h" #include "gdbcmd.h" #include <algorithm> +#include <set> struct psymbol_bcache { @@ -823,6 +824,8 @@ psym_relocate (struct objfile *objfile, if (SYMBOL_SECTION (psym) >= 0) SYMBOL_VALUE_ADDRESS (psym) += ANOFFSET (delta, SYMBOL_SECTION (psym)); } + + objfile->psymbol_map.clear (); } /* Psymtab version of find_last_source_symtab. See its definition in @@ -1458,6 +1461,84 @@ psym_has_symbols (struct objfile *objfile) return objfile->psymtabs != NULL; } +/* Helper function for psym_find_compunit_symtab_by_address that fills + in psymbol_map for a given range of psymbols. */ + +static void +psym_fill_psymbol_map (struct objfile *objfile, + struct partial_symtab *psymtab, + std::set<CORE_ADDR> *seen_addrs, + const std::vector<partial_symbol *> &symbols, + int start, + int length) +{ + for (int i = 0; i < length; ++i) + { + struct partial_symbol *psym = symbols[start + i]; + + if (PSYMBOL_CLASS (psym) == LOC_STATIC) + { + CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (psym); + if (seen_addrs->find (addr) == seen_addrs->end ()) + { + seen_addrs->insert (addr); + objfile->psymbol_map.emplace_back (addr, psymtab); + } + } + } +} + +/* See find_compunit_symtab_by_address in quick_symbol_functions, in + symfile.h. */ + +static compunit_symtab * +psym_find_compunit_symtab_by_address (struct objfile *objfile, + CORE_ADDR address) +{ + if (objfile->psymbol_map.empty ()) + { + struct partial_symtab *pst; + + std::set<CORE_ADDR> seen_addrs; + + ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, pst) + { + psym_fill_psymbol_map (objfile, pst, + &seen_addrs, + objfile->global_psymbols, + pst->globals_offset, + pst->n_global_syms); + psym_fill_psymbol_map (objfile, pst, + &seen_addrs, + objfile->static_psymbols, + pst->statics_offset, + pst->n_static_syms); + } + + objfile->psymbol_map.shrink_to_fit (); + + std::sort (objfile->psymbol_map.begin (), objfile->psymbol_map.end (), + [] (const std::pair<CORE_ADDR, partial_symtab *> &a, + const std::pair<CORE_ADDR, partial_symtab *> &b) + { + return a.first < b.first; + }); + } + + auto iter = std::lower_bound + (objfile->psymbol_map.begin (), objfile->psymbol_map.end (), address, + [] (const std::pair<CORE_ADDR, partial_symtab *> &a, + CORE_ADDR b) + { + return a.first < b; + }); + + if (iter == objfile->psymbol_map.end () || iter->first != address) + return NULL; + + return psymtab_to_symtab (objfile, iter->second); +} + const struct quick_symbol_functions psym_functions = { psym_has_symbols, @@ -1474,6 +1555,7 @@ const struct quick_symbol_functions psym_functions = psym_map_matching_symbols, psym_expand_symtabs_matching, psym_find_pc_sect_compunit_symtab, + psym_find_compunit_symtab_by_address, psym_map_symbol_filenames }; diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c index c1b2d6e..832f77f 100644 --- a/gdb/rust-lang.c +++ b/gdb/rust-lang.c @@ -29,6 +29,7 @@ #include "gdbarch.h" #include "infcall.h" #include "objfiles.h" +#include "psymtab.h" #include "rust-lang.h" #include "valprint.h" #include "varobj.h" @@ -396,6 +397,39 @@ rust_chartype_p (struct type *type) && TYPE_UNSIGNED (type)); } +/* If VALUE represents a trait object pointer, return the underlying + pointer with the correct (i.e., runtime) type. Otherwise, return + NULL. */ + +static struct value * +rust_get_trait_object_pointer (struct value *value) +{ + struct type *type = check_typedef (value_type (value)); + + if (TYPE_CODE (type) != TYPE_CODE_STRUCT || TYPE_NFIELDS (type) != 2) + return NULL; + + /* Try to be a bit resilient if the ABI changes. */ + int vtable_field = 0; + for (int i = 0; i < 2; ++i) + { + if (strcmp (TYPE_FIELD_NAME (type, i), "vtable") == 0) + vtable_field = i; + else if (strcmp (TYPE_FIELD_NAME (type, i), "pointer") != 0) + return NULL; + } + + CORE_ADDR vtable = value_as_address (value_field (value, vtable_field)); + struct symbol *symbol = find_symbol_at_address (vtable); + if (symbol == NULL || !symbol->is_rust_vtable) + return NULL; + + struct rust_vtable_symbol *vtable_sym + = static_cast<struct rust_vtable_symbol *> (symbol); + struct type *pointer_type = lookup_pointer_type (vtable_sym->concrete_type); + return value_cast (pointer_type, value_field (value, 1 - vtable_field)); +} + /* la_emitchar implementation for Rust. */ @@ -1617,6 +1651,25 @@ rust_evaluate_subexp (struct type *expect_type, struct expression *exp, switch (exp->elts[*pos].opcode) { + case UNOP_IND: + { + if (noside != EVAL_NORMAL) + result = evaluate_subexp_standard (expect_type, exp, pos, noside); + else + { + ++*pos; + struct value *value = evaluate_subexp (expect_type, exp, pos, + noside); + + struct value *trait_ptr = rust_get_trait_object_pointer (value); + if (trait_ptr != NULL) + value = trait_ptr; + + result = value_ind (value); + } + } + break; + case UNOP_COMPLEMENT: { struct value *value; diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c index 49c472e0..1754754 100644 --- a/gdb/symfile-debug.c +++ b/gdb/symfile-debug.c @@ -365,6 +365,33 @@ debug_qf_map_symbol_filenames (struct objfile *objfile, need_fullname); } +static struct compunit_symtab * +debug_qf_find_compunit_symtab_by_address (struct objfile *objfile, + CORE_ADDR address) +{ + const struct debug_sym_fns_data *debug_data + = ((const struct debug_sym_fns_data *) + objfile_data (objfile, symfile_debug_objfile_data_key)); + fprintf_filtered (gdb_stdlog, + "qf->find_compunit_symtab_by_address (%s, %s)\n", + objfile_debug_name (objfile), + hex_string (address)); + + struct compunit_symtab *result = NULL; + if (debug_data->real_sf->qf->map_symbol_filenames != NULL) + result + = debug_data->real_sf->qf->find_compunit_symtab_by_address (objfile, + address); + + fprintf_filtered (gdb_stdlog, + "qf->find_compunit_symtab_by_address (...) = %s\n", + result + ? debug_symtab_name (compunit_primary_filetab (result)) + : "NULL"); + + return result; +} + static const struct quick_symbol_functions debug_sym_quick_functions = { debug_qf_has_symbols, @@ -381,6 +408,7 @@ static const struct quick_symbol_functions debug_sym_quick_functions = debug_qf_map_matching_symbols, debug_qf_expand_symtabs_matching, debug_qf_find_pc_sect_compunit_symtab, + debug_qf_find_compunit_symtab_by_address, debug_qf_map_symbol_filenames }; diff --git a/gdb/symfile.h b/gdb/symfile.h index 3472aa0..e742984 100644 --- a/gdb/symfile.h +++ b/gdb/symfile.h @@ -270,6 +270,14 @@ struct quick_symbol_functions (struct objfile *objfile, struct bound_minimal_symbol msymbol, CORE_ADDR pc, struct obj_section *section, int warn_if_readin); + /* Return the comp unit from OBJFILE that contains a symbol at + ADDRESS. Return NULL if there is no such comp unit. Unlike + find_pc_sect_compunit_symtab, any sort of symbol (not just text + symbols) can be considered, and only exact address matches are + considered. This pointer may be NULL. */ + struct compunit_symtab *(*find_compunit_symtab_by_address) + (struct objfile *objfile, CORE_ADDR address); + /* Call a callback for every file defined in OBJFILE whose symtab is not already read in. FUN is the callback. It is passed the file's FILENAME, the file's FULLNAME (if need_fullname is non-zero), and diff --git a/gdb/symtab.c b/gdb/symtab.c index ebb7fbe..38bc713 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -2964,6 +2964,45 @@ find_pc_compunit_symtab (CORE_ADDR pc) { return find_pc_sect_compunit_symtab (pc, find_pc_mapped_section (pc)); } + +/* See symtab.h. */ + +struct symbol * +find_symbol_at_address (CORE_ADDR address) +{ + struct objfile *objfile; + + ALL_OBJFILES (objfile) + { + if (objfile->sf == NULL + || objfile->sf->qf->find_compunit_symtab_by_address == NULL) + continue; + + struct compunit_symtab *symtab + = objfile->sf->qf->find_compunit_symtab_by_address (objfile, address); + if (symtab != NULL) + { + const struct blockvector *bv = COMPUNIT_BLOCKVECTOR (symtab); + + for (int i = GLOBAL_BLOCK; i <= STATIC_BLOCK; ++i) + { + struct block *b = BLOCKVECTOR_BLOCK (bv, i); + struct block_iterator iter; + struct symbol *sym; + + ALL_BLOCK_SYMBOLS (b, iter, sym) + { + if (SYMBOL_CLASS (sym) == LOC_STATIC + && SYMBOL_VALUE_ADDRESS (sym) == address) + return sym; + } + } + } + } + + return NULL; +} + /* Find the source file and line number for a given PC value and SECTION. diff --git a/gdb/symtab.h b/gdb/symtab.h index e419d8c..9466f29 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -1062,6 +1062,11 @@ struct symbol In this case the symbol is really a "struct template_symbol". */ unsigned is_cplus_template_function : 1; + /* True if this is a Rust virtual table. In this case, the symbol + can be downcast to "struct rust_vtable_symbol". */ + + unsigned is_rust_vtable : 1; + /* Line number of this symbol's definition, except for inlined functions. For an inlined function (class LOC_BLOCK and SYMBOL_INLINED set) this is the line number of the function's call @@ -1180,6 +1185,15 @@ struct template_symbol struct symbol **template_arguments; }; +/* A symbol that represents a Rust virtual table object. */ + +struct rust_vtable_symbol : public symbol +{ + /* The concrete type for which this vtable was created; that is, in + "impl Trait for Type", this is "Type". */ + struct type *concrete_type; +}; + /* Each item represents a line-->pc (or the reverse) mapping. This is somewhat more wasteful of space than one might wish, but since only @@ -1606,6 +1620,11 @@ extern struct symbol *find_pc_function (CORE_ADDR); extern struct symbol *find_pc_sect_function (CORE_ADDR, struct obj_section *); +/* Find the symbol at the given address. Returns NULL if no symbol + found. Only exact matches for ADDRESS are considered. */ + +extern struct symbol *find_symbol_at_address (CORE_ADDR); + extern int find_pc_partial_function_gnu_ifunc (CORE_ADDR pc, const char **name, CORE_ADDR *address, CORE_ADDR *endaddr, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7334fd7..12938e7 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-11-17 Tom Tromey <tom@tromey.com> + + * gdb.rust/traits.rs: New file. + * gdb.rust/traits.exp: New file. + 2017-11-17 Joel Brobecker <brobecker@adacore.com> * gdb.ada/minsyms: New testcase. diff --git a/gdb/testsuite/gdb.rust/traits.exp b/gdb/testsuite/gdb.rust/traits.exp new file mode 100644 index 0000000..26686ef --- /dev/null +++ b/gdb/testsuite/gdb.rust/traits.exp @@ -0,0 +1,47 @@ +# Copyright (C) 2017 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 trait object printing. + +load_lib rust-support.exp +if {[skip_rust_tests]} { + continue +} + +standard_testfile .rs +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug rust}]} { + return -1 +} + +set readelf_program [gdb_find_readelf] +set result [catch "exec $readelf_program --debug-dump=info $binfile" output] +if {$result != 0} { + untested "could not read ${binfile} with readelf" + return +} + +if {![regexp DW_AT_containing_type $output]} { + untested "Rust compiler did not emit DW_AT_containing_type" + return +} + +set line [gdb_get_line_number "set breakpoint here"] +if {![runto ${srcfile}:$line]} { + untested "could not run to breakpoint" + return -1 +} + +gdb_test "print *td" " = 23.5" +gdb_test "print *tu" " = 23" diff --git a/gdb/testsuite/gdb.rust/traits.rs b/gdb/testsuite/gdb.rust/traits.rs new file mode 100644 index 0000000..98f841b --- /dev/null +++ b/gdb/testsuite/gdb.rust/traits.rs @@ -0,0 +1,37 @@ +// Copyright (C) 2017 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/>. + +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + +pub trait T { +} + +impl T for f64 { +} + +impl T for u8 { +} + +pub fn main() { + let d = 23.5f64; + let u = 23u8; + + let td = &d as &T; + let tu = &u as &T; + + println!(""); // set breakpoint here +} |