diff --git a/gdb/common/common-exceptions.c b/gdb/common/common-exceptions.c
index 2ad0ce1..b300a9d 100644
--- a/gdb/common/common-exceptions.c
+++ b/gdb/common/common-exceptions.c
@@ -22,6 +22,8 @@
 
 const struct gdb_exception exception_none = { 0, GDB_NO_ERROR, NULL };
 
+#ifndef __cplusplus
+
 /* Possible catcher states.  */
 enum catcher_state {
   /* Initial state, a new catcher has just been created.  */
@@ -185,7 +187,7 @@ exceptions_state_mc_catch (struct gdb_exception *exception,
     {
       if (mask & RETURN_MASK (exception->reason))
 	{
-	  /* Exit normally and let the called handle the
+	  /* Exit normally and let the caller handle the
 	     exception.  */
 	  return 1;
 	}
@@ -211,6 +213,56 @@ exceptions_state_mc_action_iter_1 (void)
   return exceptions_state_mc (CATCH_ITER_1);
 }
 
+#else /* !__cplusplus */
+
+/* How many nested TRY blocks we have.  See exception_messages and
+   throw_it.  */
+
+static int try_scope_depth;
+
+/* Called on entry to a TRY scope.  */
+
+void *
+exception_try_scope_entry (void)
+{
+  ++try_scope_depth;
+  return (void *) save_cleanups ();
+}
+
+/* Called on exit of a TRY scope, either normal exit or exception
+   exit.  */
+
+void
+exception_try_scope_exit (void *saved_state)
+{
+  restore_cleanups ((struct cleanup *) saved_state);
+  --try_scope_depth;
+}
+
+/* Called by the default catch block.  IOW, we'll get here before
+   jumping out to the next outermost scope an exception if a GDB
+   exception is not caught.  */
+
+void
+exception_rethrow (void)
+{
+  /* Run this scope's cleanups before re-throwing to the next
+     outermost scope.  */
+  prepare_to_throw_exception ();
+  do_cleanups (all_cleanups ());
+  throw;
+}
+
+/* Copy the 'gdb_exception' portion of FROM to TO.  */
+
+static void
+gdb_exception_sliced_copy (struct gdb_exception *to, const struct gdb_exception *from)
+{
+  *to = *from;
+}
+
+#endif /* !__cplusplus */
+
 /* Return EXCEPTION to the nearest containing catch_errors().  */
 
 void
@@ -220,12 +272,31 @@ throw_exception (struct gdb_exception exception)
 
   do_cleanups (all_cleanups ());
 
+#ifndef __cplusplus
   /* Jump to the containing catch_errors() call, communicating REASON
      to that call via setjmp's return value.  Note that REASON can't
      be zero, by definition in defs.h.  */
   exceptions_state_mc (CATCH_THROWING);
   current_catcher->exception = exception;
   SIGLONGJMP (current_catcher->buf, exception.reason);
+#else
+  if (exception.reason == RETURN_QUIT)
+    {
+      gdb_exception_RETURN_MASK_QUIT ex;
+
+      gdb_exception_sliced_copy (&ex, &exception);
+      throw ex;
+    }
+  else if (exception.reason == RETURN_ERROR)
+    {
+      gdb_exception_RETURN_MASK_ERROR ex;
+
+      gdb_exception_sliced_copy (&ex, &exception);
+      throw ex;
+    }
+  else
+    gdb_assert_not_reached ("invalid return reason");
+#endif
 }
 
 /* A stack of exception messages.
@@ -249,7 +320,11 @@ throw_it (enum return_reason reason, enum errors error, const char *fmt,
 {
   struct gdb_exception e;
   char *new_message;
+#ifndef __cplusplus
   int depth = catcher_list_size ();
+#else
+  int depth = try_scope_depth;
+#endif
 
   gdb_assert (depth > 0);
 
diff --git a/gdb/common/common-exceptions.h b/gdb/common/common-exceptions.h
index d2c0bee..2e6a6d9 100644
--- a/gdb/common/common-exceptions.h
+++ b/gdb/common/common-exceptions.h
@@ -121,10 +121,16 @@ struct gdb_exception
    the exceptions subsystem and not used other than via the TRY/CATCH
    macros defined below.  */
 
+#ifndef __cplusplus
 extern SIGJMP_BUF *exceptions_state_mc_init (void);
 extern int exceptions_state_mc_action_iter (void);
 extern int exceptions_state_mc_action_iter_1 (void);
 extern int exceptions_state_mc_catch (struct gdb_exception *, int);
+#else
+extern void *exception_try_scope_entry (void);
+extern void exception_try_scope_exit (void *saved_state);
+extern void exception_rethrow (void);
+#endif
 
 /* Macro to wrap up standard try/catch behavior.
 
@@ -151,6 +157,8 @@ extern int exceptions_state_mc_catch (struct gdb_exception *, int);
 
   */
 
+#ifndef __cplusplus
+
 #define TRY \
      { \
        SIGJMP_BUF *buf = \
@@ -168,6 +176,65 @@ extern int exceptions_state_mc_catch (struct gdb_exception *, int);
 #define END_CATCH				\
   }
 
+#else
+
+/* Prevent error/quit during TRY from calling cleanups established
+   prior to here.  This pops out the scope in either case of normal
+   exit or exception exit.  */
+struct exception_try_scope
+{
+  exception_try_scope ()
+  {
+    saved_state = exception_try_scope_entry ();
+  }
+  ~exception_try_scope ()
+  {
+    exception_try_scope_exit (saved_state);
+  }
+
+  void *saved_state;
+};
+
+/* We still need to wrap TRY/CATCH in C++ so that cleanups and C++
+   exceptions can coexist.  The TRY blocked is wrapped in a
+   do/while(0) so that break/continue within the block works the same
+   as in C.  */
+#define TRY								\
+  try									\
+    {									\
+      exception_try_scope exception_try_scope_instance;			\
+      do								\
+	{
+
+#define CATCH(EXCEPTION, MASK)						\
+	} while (0);							\
+    }								        \
+  catch (struct gdb_exception ## _ ## MASK &EXCEPTION)
+
+#define END_CATCH				\
+  catch (...)					\
+  {						\
+    exception_rethrow ();			\
+  }
+
+/* The exception types client code may catch.  They're just shims
+   around gdb_exception that add nothing but type info.  Which is used
+   is selected depending on the MASK argument passed to CATCH.  */
+
+struct gdb_exception_RETURN_MASK_ALL : public gdb_exception
+{
+};
+
+struct gdb_exception_RETURN_MASK_ERROR : public gdb_exception_RETURN_MASK_ALL
+{
+};
+
+struct gdb_exception_RETURN_MASK_QUIT : public gdb_exception_RETURN_MASK_ALL
+{
+};
+
+#endif
+
 /* *INDENT-ON* */
 
 /* Hook to allow client-specific actions to be performed prior to
