diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9b1c335..0569cda 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,27 @@
+2017-11-15  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-15  Simon Marchi  <simon.marchi@ericsson.com>
 
 	* tui/tui-win.c (window_name_completer): Replace VEC with
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..e72fbfe 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -28,6 +28,7 @@
 #include "registry.h"
 #include "gdb_bfd.h"
 #include <vector>
+#include <unordered_map>
 
 struct bcache;
 struct htab;
@@ -361,6 +362,11 @@ struct objfile
 
   struct psymbol_bcache *psymbol_cache;
 
+  /* Map symbol addresses to the partial symtab that defines the
+     object at that address.  */
+
+  std::unordered_map<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..e15a8bc 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -823,6 +823,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 +1460,56 @@ 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,
+		       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)
+	objfile->psymbol_map[SYMBOL_VALUE_ADDRESS (psym)] = 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;
+
+      ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, pst)
+      {
+	psym_fill_psymbol_map (objfile, pst,
+			       objfile->global_psymbols,
+			       pst->globals_offset,
+			       pst->n_global_syms);
+	psym_fill_psymbol_map (objfile, pst,
+			       objfile->static_psymbols,
+			       pst->statics_offset,
+			       pst->n_static_syms);
+      }
+    }
+
+  auto iter = objfile->psymbol_map.find (address);
+  if (iter == objfile->psymbol_map.end ())
+    return NULL;
+
+  return psymtab_to_symtab (objfile, iter->second);
+}
+
 const struct quick_symbol_functions psym_functions =
 {
   psym_has_symbols,
@@ -1474,6 +1526,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..701df55 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
+    = reinterpret_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 7b8b5cc..0845ae8 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1061,6 +1061,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
@@ -1179,6 +1184,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
@@ -1605,6 +1619,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 bb8dd79..9c46b74 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-11-15  Tom Tromey  <tom@tromey.com>
+
+	* gdb.rust/traits.rs: New file.
+	* gdb.rust/traits.exp: New file.
+
 2017-11-15  Simon Marchi  <simon.marchi@ericsson.com>
 
 	* gdb.tui/completionn.exp (test_tab_completion): Add space in
diff --git a/gdb/testsuite/gdb.rust/traits.exp b/gdb/testsuite/gdb.rust/traits.exp
new file mode 100644
index 0000000..5a3c942
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/traits.exp
@@ -0,0 +1,35 @@
+# 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 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
+}
