From 0eea78648ce3235fb2506f561c0345f29fc241ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gardou?= Date: Wed, 14 Apr 2021 13:36:17 +0200 Subject: [PATCH] [KDGDB] Adapt to amd64 --- drivers/base/kdgdb/CMakeLists.txt | 4 +- drivers/base/kdgdb/amd64_sup.c | 263 ++++++++++++++++++++++++++++++ drivers/base/kdgdb/gdb_input.c | 18 +- drivers/base/kdgdb/kdgdb.h | 13 +- 4 files changed, 284 insertions(+), 14 deletions(-) create mode 100644 drivers/base/kdgdb/amd64_sup.c diff --git a/drivers/base/kdgdb/CMakeLists.txt b/drivers/base/kdgdb/CMakeLists.txt index d783b3851da..05c2f8f6e2e 100644 --- a/drivers/base/kdgdb/CMakeLists.txt +++ b/drivers/base/kdgdb/CMakeLists.txt @@ -10,9 +10,11 @@ list(APPEND SOURCE utils.c kdgdb.h) -# TODO: AMD64, ARM... +# TODO: ARM... if(ARCH STREQUAL "i386") list(APPEND SOURCE i386_sup.c) +elseif(ARCH STREQUAL "amd64") + list(APPEND SOURCE amd64_sup.c) endif() add_library(kdcom MODULE diff --git a/drivers/base/kdgdb/amd64_sup.c b/drivers/base/kdgdb/amd64_sup.c new file mode 100644 index 00000000000..b497581a797 --- /dev/null +++ b/drivers/base/kdgdb/amd64_sup.c @@ -0,0 +1,263 @@ +/* + * PROJECT: ReactOS KD dll - GDB stub + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Base functions for the kernel debugger + * COPYRIGHT: Copyright 2021 Jérôme Gardou + */ + +#include "kdgdb.h" + +enum reg_name +{ + RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, + R8, R9, R10, R11, R12, R13, R14, R15, + RIP, + EFLAGS, + CS, SS, DS, ES, FS, GS, + ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, + FCTRL, FSTAT, FTAG, FISEG, FIOFF, FOSEG, FOOFF, FOP +}; + +static const unsigned char reg_size[] = +{ + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, + 4, + 4, 4, 4, 4, 4, 4, + 10, 10, 10, 10, 10, 10, 10, 10, + 8, 8, 8, 8, 8, 8, 8, 8 +}; + +static +void* +ctx_to_reg(CONTEXT* ctx, enum reg_name name) +{ + switch (name) + { + case RAX: return &ctx->Rax; + case RBX: return &ctx->Rbx; + case RCX: return &ctx->Rcx; + case RDX: return &ctx->Rdx; + case RSP: return &ctx->Rsp; + case RBP: return &ctx->Rbp; + case RSI: return &ctx->Rsi; + case RDI: return &ctx->Rdi; + case RIP: return &ctx->Rip; + case R8: return &ctx->R8; + case R9: return &ctx->R9; + case R10: return &ctx->R10; + case R11: return &ctx->R11; + case R12: return &ctx->R12; + case R13: return &ctx->R13; + case R14: return &ctx->R14; + case R15: return &ctx->R15; + case EFLAGS: return &ctx->EFlags; + case CS: return &ctx->SegCs; + case DS: return &ctx->SegSs; + case ES: return &ctx->SegEs; + case FS: return &ctx->SegFs; + case GS: return &ctx->SegGs; + case SS: return &ctx->SegSs; + } +#undef return_reg + return 0; +} + +static +void* +thread_to_reg(PETHREAD Thread, enum reg_name reg_name) +{ + static const void* NullValue = NULL; + +#if 0 + if (Thread->Tcb.TrapFrame) + { + PKTRAP_FRAME TrapFrame = Thread->Tcb.TrapFrame; + + switch (reg_name) + { + case RAX: return &TrapFrame->Rax; + case RBX: return &TrapFrame->Rbx; + case RCX: return &TrapFrame->Rcx; + case RDX: return &TrapFrame->Rdx; + case RSP: return &TrapFrame->Rsp; + case RBP: return &TrapFrame->Rbp; + case RSI: return &TrapFrame->Rsi; + case RDI: return &TrapFrame->Rdi; + case RIP: return &TrapFrame->Rip; + case R8: return &TrapFrame->R8; + case R9: return &TrapFrame->R9; + case R10: return &TrapFrame->R10; + case R11: return &TrapFrame->R11; + case EFLAGS: return &TrapFrame->EFlags; + case CS: return &TrapFrame->SegCs; + case DS: return &TrapFrame->SegSs; + case ES: return &TrapFrame->SegEs; + case FS: return &TrapFrame->SegFs; + case GS: return &TrapFrame->SegGs; + case SS: return &TrapFrame->SegSs; + default: + KDDBGPRINT("Unhandled regname: %d.\n", reg_name); + } + } + else +#endif + if (!Thread->Tcb.InitialStack) + { + /* Terminated thread ? */ + switch (reg_name) + { + case RSP: + case RBP: + case RIP: + KDDBGPRINT("Returning NULL for register %d.\n", reg_name); + return &NullValue; + default: + return NULL; + } + } + else + { + switch(reg_name) + { + case RSP: return &Thread->Tcb.KernelStack; + case RIP: + { + PULONG_PTR Rsp = Thread->Tcb.KernelStack; + return &Rsp[3]; + } + case RBP: + { + PULONG_PTR Rsp = Thread->Tcb.KernelStack; + return &Rsp[4]; + } + default: + return NULL; + } + } + + return NULL; +} + +KDSTATUS +gdb_send_registers(void) +{ + CHAR RegisterStr[17]; + UCHAR* RegisterPtr; + unsigned short i; + unsigned short size; + + start_gdb_packet(); + + KDDBGPRINT("Sending registers of thread %" PRIxPTR ".\n", gdb_dbg_tid); + KDDBGPRINT("Current thread_id: %p.\n", PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)); + if (((gdb_dbg_pid == 0) && (gdb_dbg_tid == 0)) || + gdb_tid_to_handle(gdb_dbg_tid) == PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)) + { + for (i = 0; i < 24; i++) + { + RegisterPtr = ctx_to_reg(&CurrentContext, i); + size = reg_size[i] * 2; + RegisterStr[size] = 0; + while (size) + { + size--; + RegisterStr[size] = hex_chars[RegisterPtr[size/2] & 0xF]; + size--; + RegisterStr[size] = hex_chars[RegisterPtr[size/2] >> 4]; + } + + send_gdb_partial_packet(RegisterStr); + } + } + else + { + PETHREAD DbgThread; + + DbgThread = find_thread(gdb_dbg_pid, gdb_dbg_tid); + + if (DbgThread == NULL) + { + /* Thread is dead */ + send_gdb_partial_packet("E03"); + return finish_gdb_packet(); + } + + for (i = 0; i < 24; i++) + { + RegisterPtr = thread_to_reg(DbgThread, i); + size = reg_size[i] * 2; + RegisterStr[size] = 0; + + while (size) + { + if (RegisterPtr) + { + size--; + RegisterStr[size] = hex_chars[RegisterPtr[size/2] & 0xF]; + size--; + RegisterStr[size] = hex_chars[RegisterPtr[size/2] >> 4]; + } + else + { + size--; + RegisterStr[size] = 'x'; + size--; + RegisterStr[size] = 'x'; + } + } + + send_gdb_partial_packet(RegisterStr); + } + } + + return finish_gdb_packet(); +} + +KDSTATUS +gdb_send_register(void) +{ + enum reg_name reg_name; + void *ptr; + + /* Get the GDB register name (gdb_input = "pXX") */ + reg_name = (hex_value(gdb_input[1]) << 4) | hex_value(gdb_input[2]); + + if (((gdb_dbg_pid == 0) && (gdb_dbg_tid == 0)) || + gdb_tid_to_handle(gdb_dbg_tid) == PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)) + { + /* We can get it from the context of the current exception */ + ptr = ctx_to_reg(&CurrentContext, reg_name); + } + else + { + PETHREAD DbgThread; + + DbgThread = find_thread(gdb_dbg_pid, gdb_dbg_tid); + + if (DbgThread == NULL) + { + /* Thread is dead */ + return send_gdb_packet("E03"); + } + + ptr = thread_to_reg(DbgThread, reg_name); + } + + if (!ptr) + { + unsigned char size = reg_size[reg_name]; + start_gdb_packet(); + while (size--) + send_gdb_partial_packet("xx"); + return finish_gdb_packet(); + } + else + { + KDDBGPRINT("KDDBG : Sending registers as memory.\n"); + return send_gdb_memory(ptr, reg_size[reg_name]); + } +} + + diff --git a/drivers/base/kdgdb/gdb_input.c b/drivers/base/kdgdb/gdb_input.c index 3b898c46829..23f030686d4 100644 --- a/drivers/base/kdgdb/gdb_input.c +++ b/drivers/base/kdgdb/gdb_input.c @@ -301,12 +301,6 @@ handle_gdb_query(void) return gdb_send_debug_io(&String, FALSE); } - if (strncmp(gdb_input, "qOffsets", 8) == 0) - { - /* We load ntoskrnl at 0x80800000 while compiling it at 0x00800000 base address */ - return send_gdb_packet("TextSeg=80000000"); - } - if (strcmp(gdb_input, "qTStatus") == 0) { /* No tracepoint support */ @@ -323,7 +317,7 @@ handle_gdb_query(void) { static LIST_ENTRY* CurrentEntry = NULL; char str_helper[256]; - char name_helper[64]; + char name_helper[64]; ULONG_PTR Offset = hex_to_address(&gdb_input[22]); ULONG_PTR ToSend = hex_to_address(strstr(&gdb_input[22], ",") + 1); ULONG Sent = 0; @@ -381,7 +375,7 @@ handle_gdb_query(void) /* GDB doesn't load the file if you don't prefix it with a drive letter... */ mem_length = _snprintf(str_helper, 256, "", &name_helper, DllBase); - + /* DLL name must be too long. */ if (mem_length < 0) { @@ -639,7 +633,7 @@ handle_gdb_write_mem( /* Nothing to do */ return LOOP_IF_SUCCESS(send_gdb_packet("OK")); } - + State->u.WriteMemory.TransferCount = BufferLength; MessageData->Length = BufferLength; MessageData->Buffer = (CHAR*)OutBuffer; @@ -794,7 +788,7 @@ RestoreBreakPointSendHandler( KDDBGPRINT("Wrong API number (%lu) after DbgKdRestoreBreakPointApi request.\n", State->ApiNumber); } - /* We ignore failure here. If DbgKdRestoreBreakPointApi fails, + /* We ignore failure here. If DbgKdRestoreBreakPointApi fails, * this means that the breakpoint was already invalid for KD. So clean it up on our side. */ for (i = 0; i < (sizeof(BreakPointHandles) / sizeof(BreakPointHandles[0])); i++) { @@ -879,7 +873,7 @@ handle_gdb_c( Status = send_gdb_packet("OK"); if (Status != KdPacketReceived) return Status; - + if (CurrentStateChange.NewState == DbgKdExceptionStateChange) { @@ -967,7 +961,7 @@ handle_gdb_v( if (strncmp(gdb_input, "vCont;s", 7) == 0) { - + return handle_gdb_s(State, MessageData, MessageLength, KdContext); } } diff --git a/drivers/base/kdgdb/kdgdb.h b/drivers/base/kdgdb/kdgdb.h index d09ef74593b..a260e570319 100644 --- a/drivers/base/kdgdb/kdgdb.h +++ b/drivers/base/kdgdb/kdgdb.h @@ -19,7 +19,7 @@ #include -// #define KDDEBUG /* uncomment to enable debugging this dll */ +#define KDDEBUG /* uncomment to enable debugging this dll */ /* To undefine once https://sourceware.org/bugzilla/show_bug.cgi?id=17397 is resolved */ #define MONOPROCESS 1 @@ -132,6 +132,17 @@ extern KDSTATUS gdb_send_registers(void); # define KD_BREAKPOINT_SIZE sizeof(UCHAR) # define KD_BREAKPOINT_VALUE 0xCC /* Single step mode */ +# define KdpSetSingleStep(Context) \ + ((Context)->EFlags |= EFLAGS_TF) +#elif defined(_M_AMD64) +# define KdpGetContextPc(Context) \ + ((Context)->Rip) +# define KdpSetContextPc(Context, ProgramCounter) \ + ((Context)->Rip = (ProgramCounter)) +# define KD_BREAKPOINT_TYPE UCHAR +# define KD_BREAKPOINT_SIZE sizeof(UCHAR) +# define KD_BREAKPOINT_VALUE 0xCC +/* Single step mode */ # define KdpSetSingleStep(Context) \ ((Context)->EFlags |= EFLAGS_TF) #else