diff --git a/gdb/NEWS b/gdb/NEWS
index 8176940ae1a..5c8dac12de7 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -81,6 +81,13 @@
 * New convenience variable $_inferior_thread_count contains the number
   of live threads in the current inferior.
 
+* The add-inferior, clone-inferior, and MI -add-inferior commands will
+  now give a warning, and create the new inferior without a
+  connection, when the current inferior, at the time the command is
+  given, is a core-file target.  The core-file target could never
+  really be shared between inferiors, GDB is now more vocal about what
+  is going on.
+
 * New commands
 
 maintenance set ignore-prologue-end-flag on|off
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 293bc8d4f59..4d8393a3587 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -128,6 +128,11 @@ class core_target final : public process_stratum_target
   /* See definition.  */
   void info_proc_mappings (struct gdbarch *gdbarch);
 
+  /* The core_target only works for the inferior in which it was initially
+     opened, and can't be copied to some other inferior's target_stack.  */
+  bool is_shareable () override
+  { return false; }
+
 private: /* per-core data */
 
   /* Get rid of the core inferior.  */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 566da9e7d92..bb33e60a22e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3358,8 +3358,31 @@
 remote} command to connect to some other @code{gdbserver} instance,
 use @code{run} to spawn a local program, etc.
 
+Not all connections can be shared between inferiors.  For example, the
+@code{target core} target is unique for each inferior.  That is,
+multiple inferiors can use @code{target core} at the same time, but
+each @code{target core} is different.  If you try to
+@code{add-inferior}, and the current inferior is @code{target core},
+then @value{GDBN} will give a warning and create the new inferior
+without a connection, like this:
+
+@smallexample
+(@value{GDBP}) file test1
+Reading symbols from test1...
+(@value{GDBP}) target core core.test1.433190
+[New LWP 433190]
+Core was generated by `./test1'.
+Program terminated with signal SIGSEGV, Segmentation fault.
+#0  0x0000000000401111 in foo () at test1.c:6
+6	  return *global_ptr;
+(@value{GDBP}) add-inferior
+[New inferior 2]
+warning: can't share connection 1 (core) between inferiors
+Added inferior 2
+@end smallexample
+
 @kindex clone-inferior
-@item clone-inferior [ -copies @var{n} ] [ @var{infno} ]
+@item clone-inferior [ -copies @var{n} ] [ -no-connection ] [ @var{infno} ]
 Adds @var{n} inferiors ready to execute the same program as inferior
 @var{infno}; @var{n} defaults to 1, and @var{infno} defaults to the
 number of the current inferior.  This command copies the values of the
@@ -3384,6 +3407,13 @@
 
 You can now simply switch focus to inferior 2 and run it.
 
+Like @code{add-inferior}, @code{clone-inferior} shares the connection
+with the inferior @var{infno}.  If the @var{-no-connection} option is
+given, then the new inferior will be created without a connection.  If
+the connection of inferior @var{infno} can't be shared, then
+@value{GDBN} will give a warning, and the new inferior will be created
+without a connection.
+
 @kindex remove-inferiors
 @item remove-inferiors @var{infno}@dots{}
 Removes the inferior or inferiors @var{infno}@dots{}.  It is not
@@ -37828,6 +37858,11 @@
 @code{gdbserver} instance, use @code{-exec-run} to spawn a local
 program, etc.
 
+If the connection of the current inferior cannot be shared, e.g.@: the
+@code{-target-select core} target cannot be shared between inferiors,
+then @value{GDBN} will give a warning and create the new inferior
+without a connection.
+
 The command response always has a field, @var{inferior}, whose value
 is the identifier of the thread group corresponding to the new
 inferior.
diff --git a/gdb/inferior.c b/gdb/inferior.c
index f4b9828a425..ec4c348f7e3 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -816,6 +816,22 @@ switch_to_inferior_and_push_target (inferior *new_inf,
      symbols.  */
   switch_to_inferior_no_thread (new_inf);
 
+  if (!no_connection && proc_target != nullptr
+      && !proc_target->is_shareable ())
+    {
+      if (proc_target->connection_string () != nullptr)
+	warning (_("can't share connection %d (%s %s) between inferiors"),
+		 proc_target->connection_number,
+		 proc_target->shortname (),
+		 proc_target->connection_string ());
+      else
+	warning (_("can't share connection %d (%s) between inferiors"),
+		 proc_target->connection_number,
+		 proc_target->shortname ());
+
+      proc_target = nullptr;
+    }
+
   /* Reuse the target for new inferior.  */
   if (!no_connection && proc_target != NULL)
     {
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 2917f42e624..a312cd2b9bb 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -759,9 +759,9 @@ extern struct inferior *add_inferior_with_spaces (void);
 /* Print the current selected inferior.  */
 extern void print_selected_inferior (struct ui_out *uiout);
 
-/* Switch to inferior NEW_INF, a new inferior, and unless
-   NO_CONNECTION is true, push the process_stratum_target of ORG_INF
-   to NEW_INF.  */
+/* Switch to inferior NEW_INF, a new inferior, and unless NO_CONNECTION is
+   true, or the process_stratum_target of ORG_INF is not shareable, push
+   the process_stratum_target of ORG_INF to NEW_INF.  */
 
 extern void switch_to_inferior_and_push_target
   (inferior *new_inf, bool no_connection, inferior *org_inf);
diff --git a/gdb/target.c b/gdb/target.c
index b2af51551ba..ac0608645b4 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1187,6 +1187,12 @@ target_stack::push (target_ops *t)
   if (m_stack[stratum].get () != nullptr)
     unpush (m_stack[stratum].get ());
 
+  /* If this target can't be shared, then check that the target doesn't
+     already appear on some other target stack.  */
+  if (!t->is_shareable ())
+    for (inferior *inf : all_inferiors ())
+      gdb_assert (!inf->target_is_pushed (t));
+
   /* Now add the new one.  */
   m_stack[stratum] = std::move (ref);
 
@@ -3221,6 +3227,14 @@ target_ops::fileio_readlink (struct inferior *inf, const char *filename,
 
 /* See target.h.  */
 
+bool
+target_ops::is_shareable ()
+{
+  return true;
+}
+
+/* See target.h.  */
+
 int
 target_fileio_open (struct inferior *inf, const char *filename,
 		    int flags, int mode, bool warn_if_slow, fileio_error *target_errno)
diff --git a/gdb/target.h b/gdb/target.h
index 547ee8a3bbd..30e5085a543 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1321,6 +1321,14 @@ struct target_ops
     virtual bool store_memtags (CORE_ADDR address, size_t len,
 				const gdb::byte_vector &tags, int type)
       TARGET_DEFAULT_NORETURN (tcomplain ());
+
+    /* Return true if this target can be shared on multiple target_stacks,
+       or false if this target should only appear on a single target_stack.
+       When this function returns false multiple separate instances of the
+       same target_ops sub-class can still appear on different
+       target_stacks, but the same concrete instance can only appear on a
+       single target_stack.  */
+    virtual bool is_shareable ();
   };
 
 /* Deleter for std::unique_ptr.  See comments in
diff --git a/gdb/testsuite/gdb.multi/multi-core-files-1.c b/gdb/testsuite/gdb.multi/multi-core-files-1.c
new file mode 100644
index 00000000000..f996973023e
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-core-files-1.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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/>.  */
+
+#include <stdlib.h>
+
+int
+bar ()
+{
+  abort ();
+  return 0;
+}
+
+int
+baz ()
+{
+  return bar ();
+}
+
+int
+main ()
+{
+  return baz ();
+}
diff --git a/gdb/testsuite/gdb.multi/multi-core-files-2.c b/gdb/testsuite/gdb.multi/multi-core-files-2.c
new file mode 100644
index 00000000000..fb99e137c3f
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-core-files-2.c
@@ -0,0 +1,31 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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/>.  */
+
+#include <stdlib.h>
+
+int
+foo ()
+{
+  abort ();
+  return 0;
+}
+
+int
+main ()
+{
+  return foo ();
+}
diff --git a/gdb/testsuite/gdb.multi/multi-core-files.exp b/gdb/testsuite/gdb.multi/multi-core-files.exp
new file mode 100644
index 00000000000..8c2610000a0
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-core-files.exp
@@ -0,0 +1,172 @@
+# Copyright 2022 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/>.
+
+# This script runs some basic tests that GDB can support multiple
+# inferiors each debugging different core files.
+#
+# We also check the behaviour of GDB if the user attempts to clone or
+# duplicate an inferior that is debugging a core file.
+
+standard_testfile -1.c -2.c
+
+set binfile1 "${binfile}-1"
+set binfile2 "${binfile}-2"
+
+if {[build_executable "build first executable" $binfile1 $srcfile \
+	 debug] == -1} {
+    untested "failed to compile first executable"
+    return -1
+}
+
+if {[build_executable "build second executable" $binfile2 $srcfile2 \
+	 debug] == -1} {
+    untested "failed to compile second executable"
+    return -1
+}
+
+set corefile1 [core_find $binfile1]
+set corefile2 [core_find $binfile2]
+if { $corefile1 == "" || $corefile2 == "" } {
+    untested "Can't generate core files"
+    return
+}
+
+# Start GDB, and load the first executable and corefile into the first
+# inferior.
+clean_restart ${binfile1}
+gdb_test "core-file $corefile1" "Program terminated with .*" \
+    "load core file"
+gdb_test "bt" "bar \\(\\) at .*" \
+    "check backtrace in inferior 1"
+
+# The native-extended-remote board connects to the remote target as
+# soon as GDB is started, this means that connection 1 is to the
+# remote target, and the core target we create below will be
+# connection 2.
+#
+# In all other cases, the core target gets to be connection 1.
+if { [target_info gdb_protocol] == "extended-remote"} {
+    set conn_num 2
+} else {
+    set conn_num 1
+}
+
+# Try to use add-inferior and clone-inferior to create new
+# inferiors.  In both cases this will try to share the core_target
+# between inferior 1 and the new inferior.  As the core_target can't
+# be shared we should get a warning, and the inferior should be
+# created without a connection.
+gdb_test "add-inferior" \
+    [multi_line \
+	 "\\\[New inferior 2\\\]" \
+	 "warning: can't share connection ${conn_num} \\(core\\) between inferiors" \
+	 "Added inferior 2"]
+gdb_test "clone-inferior" \
+    [multi_line \
+	 "\\\[New inferior 3\\\]" \
+	 "warning: can't share connection ${conn_num} \\(core\\) between inferiors" \
+	 "Added inferior 3"]
+
+# Check the MI -add-inferior command.  Do this using interpreter-exec.
+# We're not doing a full MI test here, just checking this one command.
+gdb_test "interpreter-exec mi \"-add-inferior\"" \
+    [multi_line \
+	 "~\"\\\[New inferior 4\\\]..\"" \
+	 "&\"warning: can't share connection ${conn_num} \\(core\\) between inferiors..\"" \
+	 "~\"Added inferior 4..\"" \
+	 "\\^done,inferior=\"\[^\"\]+\""]
+
+# Now check that none of the new inferiors have a connection.
+gdb_test "info inferiors" \
+    [multi_line \
+	 "\\*\\s+1\\s+\[^\r\n\]+\\s+${conn_num} \\(core\\)\\s+\[^\r\n\]+.*" \
+	 "\\s+2\\s+<null>\\s+" \
+	 "\\s+3\\s+<null>\\s+\[^\r\n\]+" \
+	 "\\s+4\\s+<null>\\s+"] \
+    "first info inferiors call"
+
+# Now use add-inferior and clone-inferior but this time with the
+# -no-connection option, this should avoid issuing the warning.  We
+# also use interpreter-exec to test the MI version of this command.
+gdb_test "add-inferior -no-connection" \
+    [multi_line \
+	 "\\\[New inferior 5\\\]" \
+	 "Added inferior 5"]
+gdb_test "clone-inferior -no-connection" \
+    [multi_line \
+	 "\\\[New inferior 6\\\]" \
+	 "Added inferior 6"]
+gdb_test "interpreter-exec mi \"-add-inferior --no-connection\"" \
+    "\\\[New inferior 7\\\].*Added inferior 7.*"
+
+# Now check that none of the new inferiors have a connection.
+gdb_test "info inferiors" \
+    [multi_line \
+	 "\\*\\s+1\\s+\[^\r\n\]+\\s+${conn_num} \\(core\\)\\s+\[^\r\n\]+.*" \
+	 "\\s+2\\s+<null>\\s+" \
+	 "\\s+3\\s+<null>\\s+\[^\r\n\]+" \
+	 "\\s+4\\s+<null>\\s+" \
+	 "\\s+5\\s+<null>\\s+" \
+	 "\\s+6\\s+<null>\\s+\[^\r\n\]+" \
+	 "\\s+7\\s+<null>\\s+"] \
+    "second info inferiors call"
+
+# Check after all the new inferiors have been created that we still
+# only have a single connection.
+gdb_test "info connections" \
+    "\\*\\s+${conn_num}\\s+\\s+core\\s+Local core dump file\\s*"
+
+# Now switch to inferior 2 and load the second executable and core
+# file.  Check the backtrace for the presence of function 'foo', this
+# indicates we are seeing the correct core file.
+gdb_test "inferior 2" "Switching to inferior 2 .*"
+gdb_test "file $binfile2" \
+    "Reading symbols from .*" \
+    "Loaded second test binary"
+gdb_test "core-file $corefile2" \
+    "Program terminated with signal SIGABRT, Aborted.*" \
+    "Loaded second core file"
+gdb_test "bt" "foo \\(\\) at .*" \
+    "check backtrace in inferior 2"
+
+# Switch to inferior 3, this one was cloned from inferior 1, so is
+# already debugging the first binary file.  Check its backtrace for
+# 'bar', which indicates we are debugging the correct core file.
+gdb_test "inferior 3" "Switching to inferior 3 .*"
+gdb_test "core-file $corefile1" \
+    "Program terminated with signal SIGABRT, Aborted.*" \
+    "Loaded first core file into inferior 3"
+gdb_test "bt" "bar \\(\\) at .*" \
+    "check backtrace in inferior 3"
+
+# Detach from some of the core files and delete some of the inferiors.
+gdb_test "detach" "No core file now\\." \
+    "detach from inferior 3 core file"
+gdb_test "inferior 2" "Switching to inferior 2 .*" \
+    "switch back to inferior 2"
+gdb_test_no_output "remove-inferiors 3 4"
+
+# Now detach in inferior 2, and delete the inferior.
+gdb_test "detach" "No core file now\\." \
+    "detach from inferior 2 core file"
+gdb_test "inferior 1" "Switching to inferior 1 .*" \
+    "switch back to inferior 1"
+gdb_test_no_output "remove-inferiors 2"
+
+# Finally, check that inferior 1 backtrace is still working.
+gdb_test "bt" "bar \\(\\) at .*" \
+    "check backtrace in inferior 1 again"
+gdb_test "detach" "No core file now\\." \
+    "detach from inferior 1 core file"
