diff options
Diffstat (limited to 'payloads')
-rw-r--r-- | payloads/libpayload/arch/arm/gdb.c | 18 | ||||
-rw-r--r-- | payloads/libpayload/gdb/stub.c | 30 | ||||
-rw-r--r-- | payloads/libpayload/include/gdb.h | 1 |
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 */ |