diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index f777a4a..8c14bb1 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2648,7 +2648,9 @@ insert_bp_location (struct bp_location *bl,
 	     target doesn't define error codes), so we must treat
 	     generic errors as memory errors.  */
 	  if ((bp_err == GENERIC_ERROR || bp_err == MEMORY_ERROR)
-	      && solib_name_from_address (bl->pspace, bl->address))
+	      && (solib_name_from_address (bl->pspace, bl->address)
+		  || userloaded_objfile_contains_address_p (bl->pspace,
+							    bl->address)))
 	    {
 	      /* See also: disable_breakpoints_in_shlibs.  */
 	      bl->shlib_disabled = 1;
@@ -3778,7 +3780,27 @@ remove_breakpoint_1 (struct bp_location *bl, insertion_state_t is)
 	  || !(section_is_overlay (bl->section)))
 	{
 	  /* No overlay handling: just remove the breakpoint.  */
-	  val = bl->owner->ops->remove_location (bl);
+
+	  /* If we're trying to uninsert a memory breakpoint that we
+	     know is set in a dynamic object that is marked
+	     shlib_disabled, then either the dynamic object was
+	     removed with remove-symbol-file or with
+	     "nosharedlibrary".  In the former case, we don't know
+	     whether another dynamic object might have loaded over the
+	     breakpoint's address -- the user might well let us know
+	     about it next with add-symbol-file (the whole point of
+	     OBJF_USERLOADED is letting the user manually maintain a
+	     list of dynamically loaded objects).  Check if the
+	     breakpoint is still inserted in memory, to avoid
+	     overwriting wrong code with stale saved shadow contents.
+	     HW breakpoints are independent of poking at memory and
+	     must always be removed.  */
+	  if (bl->shlib_disabled
+	      && bl->target_info.shadow_len != 0
+	      && !memory_validate_breakpoint (bl->gdbarch, &bl->target_info))
+	    val = 0;
+	  else
+	    val = bl->owner->ops->remove_location (bl);
 	}
       else
 	{
@@ -3823,10 +3845,15 @@ remove_breakpoint_1 (struct bp_location *bl, insertion_state_t is)
 	    }
 	}
 
-      /* In some cases, we might not be able to remove a breakpoint
-	 in a shared library that has already been removed, but we
-	 have not yet processed the shlib unload event.  */
-      if (val && solib_name_from_address (bl->pspace, bl->address))
+      /* In some cases, we might not be able to remove a breakpoint in
+	 a shared library that has already been removed, but we have
+	 not yet processed the shlib unload event.  Similarly for an
+	 unloaded add-symbol-file object - the user might not yet have
+	 had the chance to remove-symbol-file it.  */
+      if (val && (bl->shlib_disabled
+		  || solib_name_from_address (bl->pspace, bl->address)
+		  || userloaded_objfile_contains_address_p (bl->pspace,
+							    bl->address)))
 	val = 0;
 
       if (val)
@@ -7666,7 +7693,7 @@ disable_breakpoints_in_freed_objfile (struct objfile *objfile)
   /* If the file is a shared library not loaded by the user then
      solib_unloaded was notified and disable_breakpoints_in_unloaded_shlib
      was called.  In that case there is no need to take action again.  */
-  if ((objfile->flags & OBJF_SHARED) && !(objfile->flags & OBJF_USERLOADED))
+  if ((objfile->flags & OBJF_USERLOADED) == 0)
     return;
 
   ALL_BREAKPOINTS (b)
@@ -7698,7 +7725,11 @@ disable_breakpoints_in_freed_objfile (struct objfile *objfile)
 	  if (is_addr_in_objfile (loc_addr, objfile))
 	    {
 	      loc->shlib_disabled = 1;
-	      loc->inserted = 0;
+	      /* At this point, we don't know whether the object was
+		 unmapped from the inferior or not, so leave the
+		 inserted flag alone.  We'll handle failure to
+		 uninsert quietly, in case the object was indeed
+		 unmapped.  */
 
 	      mark_breakpoint_location_modified (loc);
 
diff --git a/gdb/mem-break.c b/gdb/mem-break.c
index 1a057df..5371bd0 100644
--- a/gdb/mem-break.c
+++ b/gdb/mem-break.c
@@ -89,3 +89,36 @@ memory_remove_breakpoint (struct target_ops *ops, struct gdbarch *gdbarch,
 {
   return gdbarch_memory_remove_breakpoint (gdbarch, bp_tgt);
 }
+
+int
+memory_validate_breakpoint (struct gdbarch *gdbarch,
+			    struct bp_target_info *bp_tgt)
+{
+  CORE_ADDR addr = bp_tgt->placed_address;
+  const gdb_byte *bp;
+  int val;
+  int bplen;
+  gdb_byte old_contents[BREAKPOINT_MAX];
+  struct cleanup *cleanup;
+  int ret;
+
+  /* Determine appropriate breakpoint contents and size for this
+     address.  */
+  bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen);
+
+  if (bp == NULL
+      || bp_tgt->placed_size != bplen)
+    return 0;
+
+  /* Make sure we see the memory breakpoints.  */
+  cleanup = make_show_memory_breakpoints_cleanup (1);
+  val = target_read_memory (addr, old_contents, bplen);
+
+  /* If our breakpoint is no longer at the address, this means that
+     the program modified the code on us, so it is wrong to put back
+     the old value.  */
+  ret = (val == 0 && memcmp (bp, old_contents, bplen) == 0);
+
+  do_cleanups (cleanup);
+  return ret;
+}
diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index e212c70..81bbf24 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -1453,6 +1453,22 @@ is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile)
   return 0;
 }
 
+int
+userloaded_objfile_contains_address_p (struct program_space *pspace,
+				       CORE_ADDR address)
+{
+  struct objfile *objfile;
+
+  ALL_PSPACE_OBJFILES (pspace, objfile)
+    {
+      if ((objfile->flags & OBJF_USERLOADED) != 0
+	  && is_addr_in_objfile (address, objfile))
+	return 1;
+    }
+
+  return 0;
+}
+
 /* The default implementation for the "iterate_over_objfiles_in_search_order"
    gdbarch method.  It is equivalent to use the ALL_OBJFILES macro,
    searching the objfiles in the order they are stored internally,
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index dc06c7b..6684278 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -515,6 +515,13 @@ extern void objfiles_changed (void);
 
 extern int is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile);
 
+/* Return true if ADDRESS maps into one of the sections of the
+   userloaded ("add-symbol-file") objfiles of PSPACE and false
+   otherwise.  */
+
+extern int userloaded_objfile_contains_address_p (struct program_space *pspace,
+						  CORE_ADDR address);
+
 /* This operation deletes all objfile entries that represent solibs that
    weren't explicitly loaded by the user, via e.g., the add-symbol-file
    command.  */
diff --git a/gdb/target.h b/gdb/target.h
index d7c6c3d..492ecb5 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2110,6 +2110,12 @@ extern int memory_remove_breakpoint (struct target_ops *, struct gdbarch *,
 extern int memory_insert_breakpoint (struct target_ops *, struct gdbarch *,
 				     struct bp_target_info *);
 
+/* Check whether the memory at the breakpoint's placed address still
+   contains the expected breakpoint instruction.  */
+
+extern int memory_validate_breakpoint (struct gdbarch *gdbarch,
+				       struct bp_target_info *bp_tgt);
+
 extern int default_memory_remove_breakpoint (struct gdbarch *,
 					     struct bp_target_info *);
 
diff --git a/gdb/testsuite/gdb.base/break-unload-file.c b/gdb/testsuite/gdb.base/break-unload-file.c
new file mode 100644
index 0000000..b0524f2
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-unload-file.c
@@ -0,0 +1,35 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014 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/>.  */
+
+void
+foo (void)
+{
+}
+
+void
+bar (void)
+{
+}
+
+int
+main (void)
+{
+  foo ();
+  bar ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/break-unload-file.exp b/gdb/testsuite/gdb.base/break-unload-file.exp
new file mode 100644
index 0000000..c3ef6a8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-unload-file.exp
@@ -0,0 +1,112 @@
+# Copyright 2014 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 "file" doesn't leave stale breakpoints planted in the
+# target.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+# Run the test proper.  ALWAYS_INSERT determines whether
+# always-inserted mode is on/off, and BREAK_COMMAND is the break
+# command being tested.
+#
+proc test_break { always_inserted break_command } {
+    global gdb_prompt binfile
+
+    with_test_prefix "always-inserted $always_inserted: $break_command" {
+	clean_restart $binfile
+
+	if ![runto_main] then {
+	    fail "Can't run to main"
+	    return
+	}
+
+	delete_breakpoints
+
+	gdb_test_no_output "set breakpoint always-inserted $always_inserted"
+
+	gdb_test "$break_command foo" ".*reakpoint .* at .*"
+
+	# Remove the file, while the breakpoint above is inserted in a
+	# function in the main objfile.  GDB used to have a bug where
+	# it would mark the breakpoint as uninserted, but actually
+	# would leave it inserted in the target.
+	set test "file"
+	gdb_test_multiple "file" $test {
+	    -re "Are you sure you want to change the file. .*y or n. $" {
+		send_gdb "y\n"
+		exp_continue
+	    }
+	    -re "Discard symbol table from `.*'? .y or n. $" {
+		send_gdb "y\n"
+		exp_continue
+	    }
+	    -re "No symbol file now\\.\r\n$gdb_prompt $" {
+		pass $test
+	    }
+	}
+
+	gdb_test "info break" "y.*PENDING.*foo"
+
+	# Now delete the breakpoint from GDB's tables, to make sure
+	# GDB doesn't reinsert it, masking the bug (with the bug, on
+	# re-insert, GDB would fill the shadow buffer with a
+	# breakpoint instruction).
+	set test "delete"
+	gdb_test_multiple $test $test {
+	    -re ".*y or n. $" {
+		send_gdb "y\n"
+		exp_continue
+	    }
+	    -re "$gdb_prompt $" {
+		pass $test
+	    }
+	}
+
+	# Re-add symbols back.
+	set test "file \$binfile"
+	gdb_test_multiple "file $binfile" $test {
+	    -re "Are you sure you want to change the file. .*y or n. $" {
+		send_gdb "y\n"
+		exp_continue
+	    }
+	    -re "Reading symbols from.*done.*$gdb_prompt $" {
+		pass $test
+	    }
+	}
+
+	# Run to another function now.  With the bug, GDB would trip
+	# on a spurious trap at foo.
+	gdb_test "b bar" ".*reakpoint .* at .*"
+	gdb_test "continue" "Breakpoint .*, bar .*"
+    }
+}
+
+# While it doesn't trigger the original bug this is a regression test
+# for, test with breakpoint always-inserted off for extra coverage.
+foreach always_inserted { "off" "on" } {
+    foreach break_command { "break" "hbreak" } {
+	test_break $always_inserted $break_command
+    }
+}
diff --git a/gdb/testsuite/gdb.base/sym-file-lib.c b/gdb/testsuite/gdb.base/sym-file-lib.c
index d0c8847..67ac9ad 100644
--- a/gdb/testsuite/gdb.base/sym-file-lib.c
+++ b/gdb/testsuite/gdb.base/sym-file-lib.c
@@ -24,3 +24,9 @@ foo (int a)
 {
   return a; /* gdb break at foo */
 }
+
+extern int
+baz (int a)
+{
+  return a; /* gdb break at baz */
+}
diff --git a/gdb/testsuite/gdb.base/sym-file-loader.c b/gdb/testsuite/gdb.base/sym-file-loader.c
index d10065e..0b0039d 100644
--- a/gdb/testsuite/gdb.base/sym-file-loader.c
+++ b/gdb/testsuite/gdb.base/sym-file-loader.c
@@ -60,6 +60,7 @@ sizeof ((hdr)->field) == sizeof (Elf_Addr) ? *(Elf_Addr *) (hdr)->field : \
 struct segment
 {
   uint8_t *mapped_addr;
+  size_t mapped_size;
   Elf_External_Phdr *phdr;
   struct segment *next;
 };
@@ -101,6 +102,7 @@ load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg)
 {
   struct segment *seg = NULL;
   uint8_t *mapped_addr = NULL;
+  size_t mapped_size = 0;
   void *from = NULL;
   void *to = NULL;
 
@@ -110,6 +112,7 @@ load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg)
   mapped_addr = (uint8_t *) mmap ((void *) GETADDR (phdr, p_vaddr),
 				  GET (phdr, p_memsz), perm,
 				  MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  mapped_size = GET (phdr, p_memsz);
 
   from = (void *) (addr + GET (phdr, p_offset));
   to = (void *) mapped_addr;
@@ -122,6 +125,7 @@ load (uint8_t *addr, Elf_External_Phdr *phdr, struct segment *tail_seg)
     return 0;
 
   seg->mapped_addr = mapped_addr;
+  seg->mapped_size = mapped_size;
   seg->phdr = phdr;
   seg->next = 0;
 
@@ -173,6 +177,30 @@ get_origin (void)
     return self_path;
 }
 
+/* Unload/unmap a segment.  */
+
+static void
+unload (struct segment *seg)
+{
+  munmap (seg->mapped_addr, seg->mapped_size);
+  free (seg);
+}
+
+void
+unload_shlib (struct library *lib)
+{
+  struct segment *seg, *next_seg;
+
+  for (seg = lib->segments; seg != NULL; seg = next_seg)
+    {
+      next_seg = seg->next;
+      unload (seg);
+    }
+
+  close (lib->fd);
+  free (lib);
+}
+
 /* Mini shared library loader.  No reallocation
    is performed for the sake of simplicity.  */
 
diff --git a/gdb/testsuite/gdb.base/sym-file-loader.h b/gdb/testsuite/gdb.base/sym-file-loader.h
index c6b1af3..7798a40 100644
--- a/gdb/testsuite/gdb.base/sym-file-loader.h
+++ b/gdb/testsuite/gdb.base/sym-file-loader.h
@@ -25,6 +25,10 @@ struct library;
 
 struct library *load_shlib (const char *file);
 
+/* Unload a library.  */
+
+void unload_shlib (struct library *lib);
+
 /* Lookup the address of FUNC.  */
 
 int lookup_function (struct library *lib, const char *func, void **addr);
diff --git a/gdb/testsuite/gdb.base/sym-file-main.c b/gdb/testsuite/gdb.base/sym-file-main.c
index b48a1c2..b6e36df 100644
--- a/gdb/testsuite/gdb.base/sym-file-main.c
+++ b/gdb/testsuite/gdb.base/sym-file-main.c
@@ -42,6 +42,8 @@ main (int argc, const char *argv[])
   char *text_addr = NULL;
   int (*pbar) () = NULL;
   int (*pfoo) (int) = NULL;
+  int (*pbaz) () = NULL;
+  int i;
 
   lib = load_shlib (file);
   if (lib == NULL)
@@ -64,8 +66,28 @@ main (int argc, const char *argv[])
 
   (*pfoo) (2);
 
-  /* Notify GDB to remove the symbol file.  */
+  /* Unload the library, invalidating all memory breakpoints.  */
+  unload_shlib (lib);
+
+  /* Notify GDB to remove the symbol file.  Also check that GDB
+     doesn't complain that it can't remove breakpoints from the
+     unmapped library.  */
   gdb_remove_symbol_file (text_addr);
 
-  return 0;
+  /* Reload the library.  */
+  lib = load_shlib (file); /* reload lib here */
+  if (lib == NULL)
+    return 1;
+
+  if (get_text_addr (lib,  (void **) &text_addr) != 0)
+    return 1;
+
+  gdb_add_symbol_file (text_addr, file);
+
+  if (lookup_function (lib, "baz", (void *) &pbaz) != 0)
+    return 1;
+
+  (*pbaz) ();
+
+  return 0; /* end here */
 }
diff --git a/gdb/testsuite/gdb.base/sym-file.exp b/gdb/testsuite/gdb.base/sym-file.exp
index c87c3c7..48fb193 100644
--- a/gdb/testsuite/gdb.base/sym-file.exp
+++ b/gdb/testsuite/gdb.base/sym-file.exp
@@ -27,6 +27,7 @@
 # 11) 'info files' must not display ${lib_basename}, anymore.
 # 12) Check that the breakpoints at foo and bar are pending.
 # 13) Check that the execution can continue without error.
+# 14) Regression test for a stale breakpoints bug.
 
 if {![is_elf_target]} {
     return 0
@@ -159,4 +160,50 @@ gdb_test "info breakpoints 4" \
 	 "breakpoint at bar is pending"
 
 # 13) Check that the execution can continue without error.
-gdb_continue_to_end
+set lnum_reload [gdb_get_line_number "reload lib here"]
+gdb_breakpoint $lnum_reload
+gdb_continue_to_breakpoint reload ".*${srcfile}:$lnum_reload.*"
+
+# 14) Regression test for a stale breakpoints bug.  Check whether
+# unloading symbols manually without the program actually unloading
+# the library, when breakpoints are inserted doesn't leave stale
+# breakpoints behind.
+with_test_prefix "stale bkpts" {
+    # Force breakpoints always inserted.
+    gdb_test_no_output "set breakpoint always-inserted on"
+
+    # Get past the library reload.
+    gdb_continue_to_breakpoint gdb_add_symbol_file
+
+    # Load the library's symbols.
+    gdb_test "add-symbol-file ${lib_syms} addr" \
+	"Reading symbols from .*${lib_syms}\\.\\.\\.done\\." \
+	"add-symbol-file ${lib_basename}.so addr" \
+	"add symbol table from file \".*${lib_syms}\"\
+at.*\\(y or n\\) " \
+	"y"
+
+    # Set a breakpoint at baz, in the library.
+    gdb_breakpoint baz
+
+    gdb_test "info breakpoints 7" ".*y.*0x.*in baz.*" \
+	"breakpoint at baz is resolved"
+
+    # Unload symbols manually without the program actually unloading
+    # the library.
+    gdb_test "remove-symbol-file -a addr" \
+	"" \
+	"remove-symbol-file -a addr" \
+	"Remove symbol table from file \".*${lib_basename}\\.so\"\\?\
+.*\\(y or n\\) " \
+	"y"
+
+    gdb_test "info breakpoints 7" ".*PENDING.*" \
+	"breakpoint at baz is pending"
+
+    # Check that execution can continue without error.  If GDB leaves
+    # breakpoints behind, we'll get back a spurious SIGTRAP.
+    set lnum_end [gdb_get_line_number "end here"]
+    gdb_breakpoint $lnum_end
+    gdb_continue_to_breakpoint "end here" ".*end here.*"
+}
