Reimplemented the gdb stub support for the 'g' command (read registers):

* Added an arch_debug_gdb_get_registers() interface that is supposed to provide
  the register values in the format expected by gdb and implemented it for x86.
* Reimplemented gdb_regreply() to use that. Also made it buffer overflow safe.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41880 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2011-06-03 15:35:10 +00:00
parent bb2420a474
commit 9536ec0297
7 changed files with 109 additions and 38 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <ByteOrder.h>
#include <TypeConstants.h>
#include <cpu.h>
@ -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)
{

View File

@ -16,6 +16,7 @@
#include <ByteOrder.h>
#include <arch/debug.h>
#include <arch/debug_console.h>
#include <debug.h>
#include <elf.h>
@ -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