diff --git a/headers/private/kernel/arch/debug.h b/headers/private/kernel/arch/debug.h index e73ada344e..b2e1681302 100644 --- a/headers/private/kernel/arch/debug.h +++ b/headers/private/kernel/arch/debug.h @@ -50,6 +50,8 @@ bool arch_is_debug_variable_defined(const char* variableName); status_t arch_set_debug_variable(const char* variableName, uint64 value); status_t arch_get_debug_variable(const char* variableName, uint64* value); +ssize_t arch_debug_gdb_get_registers(char* buffer, size_t bufferSize); + #ifdef __cplusplus } #endif diff --git a/src/system/kernel/arch/arm/arch_debug.cpp b/src/system/kernel/arch/arm/arch_debug.cpp index 5b4cd603df..1951b7b085 100644 --- a/src/system/kernel/arch/arm/arch_debug.cpp +++ b/src/system/kernel/arch/arm/arch_debug.cpp @@ -334,3 +334,9 @@ arch_debug_unset_current_thread(void) } +ssize_t +arch_debug_gdb_get_registers(char* buffer, size_t bufferSize) +{ + // TODO: Implement! + return B_NOT_SUPPORTED; +} diff --git a/src/system/kernel/arch/m68k/arch_debug.cpp b/src/system/kernel/arch/m68k/arch_debug.cpp index 23175d60d6..5ddbcdab10 100644 --- a/src/system/kernel/arch/m68k/arch_debug.cpp +++ b/src/system/kernel/arch/m68k/arch_debug.cpp @@ -398,6 +398,14 @@ arch_get_debug_variable(const char* variableName, uint64* value) } +ssize_t +arch_debug_gdb_get_registers(char* buffer, size_t bufferSize) +{ + // TODO: Implement! + return B_NOT_SUPPORTED; +} + + status_t arch_debug_init(kernel_args *args) { diff --git a/src/system/kernel/arch/mipsel/arch_debug.cpp b/src/system/kernel/arch/mipsel/arch_debug.cpp index b152cd2640..03f0ac4950 100644 --- a/src/system/kernel/arch/mipsel/arch_debug.cpp +++ b/src/system/kernel/arch/mipsel/arch_debug.cpp @@ -99,6 +99,14 @@ arch_get_debug_variable(const char* variableName, uint64* value) } +ssize_t +arch_debug_gdb_get_registers(char* buffer, size_t bufferSize) +{ + // TODO: Implement! + return B_NOT_SUPPORTED; +} + + status_t arch_debug_init(kernel_args* args) { diff --git a/src/system/kernel/arch/ppc/arch_debug.cpp b/src/system/kernel/arch/ppc/arch_debug.cpp index ef43956724..99c8c304d1 100644 --- a/src/system/kernel/arch/ppc/arch_debug.cpp +++ b/src/system/kernel/arch/ppc/arch_debug.cpp @@ -336,6 +336,14 @@ arch_get_debug_variable(const char* variableName, uint64* value) } +ssize_t +arch_debug_gdb_get_registers(char* buffer, size_t bufferSize) +{ + // TODO: Implement! + return B_NOT_SUPPORTED; +} + + status_t arch_debug_init(kernel_args *args) { diff --git a/src/system/kernel/arch/x86/arch_debug.cpp b/src/system/kernel/arch/x86/arch_debug.cpp index f4e7a9bb35..1da75efaad 100644 --- a/src/system/kernel/arch/x86/arch_debug.cpp +++ b/src/system/kernel/arch/x86/arch_debug.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -1168,6 +1169,58 @@ arch_get_debug_variable(const char* variableName, uint64* value) } +/*! Writes the contents of the CPU registers at some fixed outer stack frame or + iframe into the given buffer in the format expected by gdb. + + This function is called in response to gdb's 'g' command. + + \param buffer The buffer to write the registers to. + \param bufferSize The size of \a buffer in bytes. + \return When successful, the number of bytes written to \a buffer, or a + negative error code on error. +*/ +ssize_t +arch_debug_gdb_get_registers(char* buffer, size_t bufferSize) +{ + struct iframe* frame = get_current_iframe(debug_get_debugged_thread()); + if (frame == NULL) + return B_NOT_SUPPORTED; + + // For x86 the register order is: + // + // eax, ebx, ecx, edx, + // esp, ebp, esi, edi, + // eip, eflags, + // cs, ss, ds, es + // + // Note that even though the segment descriptors are actually 16 bits wide, + // gdb requires them as 32 bit integers. Note also that for some reason + // gdb wants the register dump in *big endian* format. + static const int32 kRegisterCount = 14; + uint32 registers[kRegisterCount] = { + frame->eax, frame->ebx, frame->ecx, frame->edx, + frame->esp, frame->ebp, frame->esi, frame->edi, + frame->eip, frame->flags, + frame->cs, frame->ds, frame->ds, frame->es + // assume ss == ds + }; + + const char* const bufferStart = buffer; + + for (int32 i = 0; i < kRegisterCount; i++) { + int result = snprintf(buffer, bufferSize, "%08" B_PRIx32, + B_HOST_TO_BENDIAN_INT32(registers[i])); + if (result >= (int)bufferSize) + return B_BUFFER_OVERFLOW; + + buffer += result; + bufferSize -= result; + } + + return buffer - bufferStart; +} + + status_t arch_debug_init(kernel_args *args) { diff --git a/src/system/kernel/debug/gdb.cpp b/src/system/kernel/debug/gdb.cpp index 72b165e910..81ba8a0be5 100644 --- a/src/system/kernel/debug/gdb.cpp +++ b/src/system/kernel/debug/gdb.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -107,23 +108,34 @@ gdb_reply(char const* format, ...) static void -gdb_regreply(int const* regs, int numregs) +gdb_regreply() { - int i; - int len; - int sum; - sReply[0] = '$'; - for (i = 0; i < numregs; i++) - sprintf(sReply + 1 + 8 * i, "%08lx", B_HOST_TO_BENDIAN_INT32(regs[i])); - len = strlen(sReply); - sum = 0; - for (i = 1; i < len; i++) + // get registers (architecture specific) + ssize_t bytesWritten = arch_debug_gdb_get_registers(sReply + 1, + sizeof(sReply) - 1); + if (bytesWritten < 0) { + gdb_reply("E01"); + return; + } + + // add 1 for the leading '$' + bytesWritten++; + + // compute check sum + int sum = 0; + for (int32 i = 1; i < bytesWritten; i++) sum += sReply[i]; sum %= 256; - sprintf(sReply + len, "#%02x", sum); + // print check sum + int result = snprintf(sReply + bytesWritten, sizeof(sReply) - bytesWritten, + "#%02x", sum); + if (result >= (ssize_t)sizeof(sReply) - bytesWritten) { + gdb_reply("E01"); + return; + } gdb_resend_reply(); } @@ -227,34 +239,8 @@ gdb_parse_command(void) return QUIT; case 'g': - { -#if 0 - int cpu; - - // command 'g' is used for reading the register - // file. Faked by now. - // - // For x86 the register order is: - // - // eax, ebx, ecx, edx, - // esp, ebp, esi, edi, - // eip, eflags, - // cs, ss, ds, es - // - // Note that even thought the segment descriptors - // are actually 16 bits wide, gdb requires them - // as 32 bit integers. Note also that for some - // reason (unknown to me) gdb wants the register - // dump in *big endian* format. - cpu = smp_get_current_cpu(); - gdb_regreply(dbg_register_file[cpu], 14); -#else - (void)gdb_regreply; - gdb_reply("E01"); -#endif - + gdb_regreply(); break; - } case 'G': // write registers