summaryrefslogtreecommitdiff
path: root/payloads
diff options
context:
space:
mode:
Diffstat (limited to 'payloads')
-rw-r--r--payloads/libpayload/arch/arm/gdb.c18
-rw-r--r--payloads/libpayload/gdb/stub.c30
-rw-r--r--payloads/libpayload/include/gdb.h1
3 files changed, 32 insertions, 17 deletions
diff --git a/payloads/libpayload/arch/arm/gdb.c b/payloads/libpayload/arch/arm/gdb.c
index 830382e8fa..2a8eb310b2 100644
--- a/payloads/libpayload/arch/arm/gdb.c
+++ b/payloads/libpayload/arch/arm/gdb.c
@@ -39,23 +39,7 @@ static struct exception_state sentinel_exception_state;
static int gdb_exception_hook(u32 type)
{
- /*
- * If we were not resumed we are in deep trouble here. GDB probably told
- * us to do something stupid and caused a reentrant exception. All we
- * can do is just blindly send an error code and keep going. Eventually
- * GDB will tell us to resume and we return right back to the original
- * exception state ("jumping over" all the nested ones).
- */
- if (gdb_state.connected && !gdb_state.resumed) {
- static const char error_code[] = "E22"; /* EINVAL? */
- static const struct gdb_message tmp_reply = {
- .buf = (u8 *)error_code,
- .used = sizeof(error_code),
- .size = sizeof(error_code),
- };
- gdb_send_reply(&tmp_reply);
- gdb_command_loop(gdb_state.signal); /* preserve old signal */
- } else {
+ if (!gdb_handle_reentrant_exception()) {
if (type >= ARRAY_SIZE(type_to_signal) || !type_to_signal[type])
return 0;
exception_state_ptr = &sentinel_exception_state;
diff --git a/payloads/libpayload/gdb/stub.c b/payloads/libpayload/gdb/stub.c
index e2979850d9..694577e8d7 100644
--- a/payloads/libpayload/gdb/stub.c
+++ b/payloads/libpayload/gdb/stub.c
@@ -118,3 +118,33 @@ void gdb_exit(s8 exit_status)
gdb_state.connected = 0;
printf("Detached from GDB connection.\n");
}
+
+/*
+ * This is a check architecture backends can run before entering the GDB command
+ * loop during exception handling. If it returns true, GDB was already running
+ * and must have caused an exception itself, which may happen if the GDB server
+ * tells us to do something stupid (e.g. write to an unmapped address). In that
+ * case, all we can do is blindly send a generic error code (since we're not
+ * sure which command caused the exception) and continue serving commands. When
+ * GDB eventually tells us to resume, we'll return from this function to the
+ * architecture backend which will have to do a "super exception return" that
+ * returns right back from the original (outermost) exception, "jumping over"
+ * all the intermediate exception frames we may have accumulated since. (This is
+ * the best we can do because our architecture backends generally don't support
+ * "full", unlimited exception reentrancy.)
+ */
+int gdb_handle_reentrant_exception(void)
+{
+ if (!gdb_state.connected || gdb_state.resumed)
+ return 0; /* This is not a reentrant exception. */
+
+ static const char error_code[] = "E22"; /* EINVAL? */
+ static const struct gdb_message tmp_reply = {
+ .buf = (u8 *)error_code,
+ .used = sizeof(error_code),
+ .size = sizeof(error_code),
+ };
+ gdb_send_reply(&tmp_reply);
+ gdb_command_loop(gdb_state.signal); /* preserve old signal */
+ return 1;
+}
diff --git a/payloads/libpayload/include/gdb.h b/payloads/libpayload/include/gdb.h
index 8147431105..1f50491662 100644
--- a/payloads/libpayload/include/gdb.h
+++ b/payloads/libpayload/include/gdb.h
@@ -77,6 +77,7 @@ void gdb_send_reply(const struct gdb_message *reply);
/* gdb/stub.c */
void gdb_command_loop(uint8_t signal);
+int gdb_handle_reentrant_exception(void);
enum {
GDB_SIG0 = 0, /* Signal 0 */