/* readline defines this. */
#undef savestring
-static void rl_callback_read_char_wrapper (gdb_client_data client_data);
static void command_line_handler (char *rl);
static void change_line_handler (void);
static char *top_level_prompt (void);
#endif
static struct async_signal_handler *async_sigterm_token;
-/* This hook is called by rl_callback_read_char_wrapper after each
+/* This hook is called by gdb_rl_callback_read_char_wrapper after each
character is processed. */
void (*after_char_processing_hook) (void);
\f
-/* Wrapper function for calling into the readline library. The event
- loop expects the callback function to have a paramter, while
- readline expects none. */
+/* Wrapper function for calling into the readline library. This takes
+ care of a couple things:
+
+ - The event loop expects the callback function to have a parameter,
+ while readline expects none.
+
+ - Propagation of GDB exceptions/errors thrown from INPUT_HANDLER
+ across readline requires special handling.
+
+ On the exceptions issue:
+
+ DWARF-based unwinding cannot cross code built without -fexceptions.
+ Any exception that tries to propagate through such code will fail
+ and the result is a call to std::terminate. While some ABIs, such
+ as x86-64, require all code to be built with exception tables,
+ others don't.
+
+ This is a problem when GDB calls some non-EH-aware C library code,
+ that calls into GDB again through a callback, and that GDB callback
+ code throws a C++ exception. Turns out this is exactly what
+ happens with GDB's readline callback.
+
+ In such cases, we must catch and save any C++ exception that might
+ be thrown from the GDB callback before returning to the
+ non-EH-aware code. When the non-EH-aware function itself returns
+ back to GDB, we then rethrow the original C++ exception.
+
+ In the readline case however, the right thing to do is to longjmp
+ out of the callback, rather than do a normal return -- there's no
+ way for the callback to return to readline an indication that an
+ error happened, so a normal return would have rl_callback_read_char
+ potentially continue processing further input, redisplay the
+ prompt, etc. Instead of raw setjmp/longjmp however, we use our
+ sjlj-based TRY/CATCH mechanism, which knows to handle multiple
+ levels of active setjmp/longjmp frames, needed in order to handle
+ the readline callback recursing, as happens with e.g., secondary
+ prompts / queries, through gdb_readline_wrapper. */
+
static void
-rl_callback_read_char_wrapper (gdb_client_data client_data)
+gdb_rl_callback_read_char_wrapper (gdb_client_data client_data)
{
- rl_callback_read_char ();
- if (after_char_processing_hook)
- (*after_char_processing_hook) ();
+ struct gdb_exception gdb_expt = exception_none;
+
+ /* C++ exceptions can't normally be thrown across readline (unless
+ it is built with -fexceptions, but it won't by default on many
+ ABIs). So we instead wrap the readline call with a sjlj-based
+ TRY/CATCH, and rethrow the GDB exception once back in GDB. */
+ TRY_SJLJ
+ {
+ rl_callback_read_char ();
+ if (after_char_processing_hook)
+ (*after_char_processing_hook) ();
+ }
+ CATCH_SJLJ (ex, RETURN_MASK_ALL)
+ {
+ gdb_expt = ex;
+ }
+ END_CATCH_SJLJ
+
+ /* Rethrow using the normal EH mechanism. */
+ if (gdb_expt.reason < 0)
+ throw_exception (gdb_expt);
+}
+
+/* GDB's readline callback handler. Calls the current INPUT_HANDLER,
+ and propagates GDB exceptions/errors thrown from INPUT_HANDLER back
+ across readline. See gdb_rl_callback_read_char_wrapper. */
+
+static void
+gdb_rl_callback_handler (char *rl)
+{
+ struct gdb_exception gdb_rl_expt = exception_none;
+
+ TRY
+ {
+ input_handler (rl);
+ }
+ CATCH (ex, RETURN_MASK_ALL)
+ {
+ gdb_rl_expt = ex;
+ }
+ END_CATCH
+
+ /* If we caught a GDB exception, longjmp out of the readline
+ callback. There's no other way for the callback to signal to
+ readline that an error happened. A normal return would have
+ readline potentially continue processing further input, redisplay
+ the prompt, etc. (This is what GDB historically did when it was
+ a C program.) Note that since we're long jumping, local variable
+ dtors are NOT run automatically. */
+ if (gdb_rl_expt.reason < 0)
+ throw_exception_sjlj (gdb_rl_expt);
}
/* Initialize all the necessary variables, start the event loop,
if (async_command_editing_p)
{
/* Turn on editing by using readline. */
- call_readline = rl_callback_read_char_wrapper;
+ call_readline = gdb_rl_callback_read_char_wrapper;
input_handler = command_line_handler;
}
else
therefore loses input. */
gdb_assert (!callback_handler_installed);
- rl_callback_handler_install (prompt, input_handler);
+ rl_callback_handler_install (prompt, gdb_rl_callback_handler);
callback_handler_installed = 1;
}
beginning. */
const char suffix[] = "\n\032\032prompt\n";
- return concat (prefix, prompt, suffix, NULL);
+ return concat (prefix, prompt, suffix, (char *) NULL);
}
return xstrdup (prompt);
it may be quite a while before we get back to the event loop. So
set quit_flag to 1 here. Then if QUIT is called before we get to
the event loop, we will unwind as expected. */
-
set_quit_flag ();
- /* If immediate_quit is set, we go ahead and process the SIGINT right
- away, even if we usually would defer this to the event loop. The
- assumption here is that it is safe to process ^C immediately if
- immediate_quit is set. If we didn't, SIGINT would be really
- processed only the next time through the event loop. To get to
- that point, though, the command that we want to interrupt needs to
- finish first, which is unacceptable. If immediate quit is not set,
- we process SIGINT the next time through the loop, which is fine. */
- gdb_call_async_signal_handler (sigint_token, immediate_quit);
+ /* In case nothing calls QUIT before the event loop is reached, the
+ event loop handles it. */
+ mark_async_signal_handler (sigint_token);
}
/* See gdb_select.h. */
}
/* Set things up for readline to be invoked via the alternate
- interface, i.e. via a callback function (rl_callback_read_char),
- and hook up instream to the event loop. */
+ interface, i.e. via a callback function
+ (gdb_rl_callback_read_char), and hook up instream to the event
+ loop. */
+
void
gdb_setup_readline (void)
{
/* When a character is detected on instream by select or poll,
readline will be invoked via this callback function. */
- call_readline = rl_callback_read_char_wrapper;
+ call_readline = gdb_rl_callback_read_char_wrapper;
}
else
{