the code for the gdb proxy. the way to use it: 0/ invoke winegdb with winegdb -- --gdb my_pgm you can also add options for my_pgm basically, winegdb will: 1/ start my_pgm 2/ start gdb and tell gdb to make a remote connection to winedbg (acting as a server for the gdb remote debugging protocol) 3/ then winedbg will act as a proxy and transform all the requests from gdb into Win32 Api calls in step 0, you can also use instead of the name of a program to be started the (Win32-)pid of a running program ------------------------ there are a few available options when starting winedbg in the gdb proxy mode: --no-start this will not trigger the step 2 above. this is useful when using another front end to winegdb (like ddd). see below --with-xterm this will start gdb in its own xterm window so that gdb output isn't mixed up with the program output (and the proxy one...) ------------------------ a few commands have been added to gdb (using the monitor command): monitor wnd monitor window lists all known window in the system monitor proc monitor process lists all known processes in the system monitor mem displays the mapping of memory of the debugged process. relies on a correct implementation of VirtualQueryEx which isn't in Wine yet two other commands exist, but their lifetime is unknown: monitor trace let you decide on the level of trace you want for the proxy monitor linear transform a segmented address into a linear one (in the debugged process context) ------------------------ using the proxy with another front-end I only tested ddd (so YMMV with GVD, emacs...) basically, most of the graphical front ends have a way to tell to use gdb in a remote way for ddd: a/ start winedbg with the --no-start option. you'll get a message like "target remote localhost:12345" (port number will vary) b/ start ddd c/ in the console window of ddd, type: file /home/eric/wine/miscemu/wine (change of course with the path to wine) target remote localhost:12345 (this should be exact message as output in step a) sharedlibrary and this should be it... as of today, the steps abc are done automatically for gdb... some future version are likely to have their own configuration options to let you choose the front end you'd like A+
Name: wdbg_gdb ChangeLog: added a remote proxy for gdb License: X11 GenDate: 2002/07/09 20:29:11 UTC ModifiedFiles: debugger-merge/Makefile.in debugger-merge/stabs.c AddedFiles: debugger-merge/gdbproxy.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/debugger/Makefile.in,v retrieving revision 1.29 diff -u -u -r1.29 Makefile.in --- debugger-merge/Makefile.in 21 May 2002 19:42:29 -0000 1.29 +++ debugger-merge/Makefile.in 21 May 2002 20:03:50 -0000 @@ -1,4 +1,4 @@ -EXTRADEFS = -DSTRICT + TOPSRCDIR = @top_srcdir@ TOPOBJDIR = .. SRCDIR = @srcdir@ @@ -14,6 +14,7 @@ display.c \ expr.c \ ext_debugger.c \ + gdbproxy.c \ hash.c \ info.c \ memory.c \ Index: debugger-merge/stabs.c =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/debugger/stabs.c,v retrieving revision 1.48 diff -u -u -r1.48 stabs.c --- debugger-merge/stabs.c 31 May 2002 23:06:47 -0000 1.48 +++ debugger-merge/stabs.c 8 Jun 2002 15:27:35 -0000 @@ -82,7 +82,8 @@ #define N_EXCL 0xc2 #define N_RBRAC 0xe0 -typedef struct tagELF_DBG_INFO { +typedef struct tagELF_DBG_INFO +{ unsigned long elf_addr; } ELF_DBG_INFO; @@ -1099,7 +1100,7 @@ int stabsect; int stabstrsect; - if (module->type != DMT_ELF || ! module->elf_info) { + if (module->type != DMT_ELF || !module->elf_info) { DEBUG_Printf(DBG_CHN_ERR, "Bad elf module '%s'\n", module->module_name); return DIL_ERROR; } @@ -1192,7 +1193,7 @@ unsigned int load_offset, unsigned int* dyn_addr) { - static const unsigned char elf_signature[4] = { 0x7f, 'E', 'L', 'F' }; + static const unsigned char elf_signature[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 }; enum DbgInfoLoad dil = DIL_ERROR; char* addr = (char*)0xffffffff; int fd = -1; @@ -1249,14 +1250,17 @@ size = ppnt[i].p_vaddr - delta + ppnt[i].p_memsz; } - for (i = 0; i < ehptr->e_shnum; i++) { + for (i = 0; i < ehptr->e_shnum; i++) + { if (strcmp(shstrtab + spnt[i].sh_name, ".bss") == 0 && - spnt[i].sh_type == SHT_NOBITS) { + spnt[i].sh_type == SHT_NOBITS) + { if (size < spnt[i].sh_addr - delta + spnt[i].sh_size) size = spnt[i].sh_addr - delta + spnt[i].sh_size; } if (strcmp(shstrtab + spnt[i].sh_name, ".dynamic") == 0 && - spnt[i].sh_type == SHT_DYNAMIC) { + spnt[i].sh_type == SHT_DYNAMIC) + { if (dyn_addr) *dyn_addr = spnt[i].sh_addr; } } @@ -1329,6 +1333,8 @@ dil = DEBUG_ProcessElfFileFromPath(filename, load_offset, dyn_addr, getenv("PATH")); if (dil == DIL_ERROR) dil = DEBUG_ProcessElfFileFromPath(filename, load_offset, dyn_addr, getenv("LD_LIBRARY_PATH")); + if (dil == DIL_ERROR) + dil = DEBUG_ProcessElfFileFromPath(filename, load_offset, dyn_addr, getenv("WINEDLLPATH")); } DEBUG_ReportDIL(dil, "ELF", filename, load_offset); @@ -1435,6 +1441,46 @@ leave: return dil; +} + +/* FIXME: merge with some of the routines above */ +int read_elf_info(const char* filename, unsigned long tab[]) +{ + static const unsigned char elf_signature[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 }; + char* addr; + Elf32_Ehdr* ehptr; + Elf32_Shdr* spnt; + char* shstrtab; + int i; + int ret = 0; + HANDLE hFile; + HANDLE hMap = 0; + + addr = NULL; + hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) goto leave; + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMap == 0) goto leave; + addr = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if (addr == NULL) goto leave; + + ehptr = (Elf32_Ehdr*) addr; + if (memcmp(ehptr->e_ident, elf_signature, sizeof(elf_signature))) goto leave; + + spnt = (Elf32_Shdr*) (addr + ehptr->e_shoff); + shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset); + + tab[0] = tab[1] = tab[2] = 0; + for (i = 0; i < ehptr->e_shnum; i++) + { + } + ret = 1; + leave: + if (addr != NULL) UnmapViewOfFile(addr); + if (hMap != 0) CloseHandle(hMap); + if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); + return ret; } #else /* !__ELF__ */ --- /dev/null Thu Jan 1 01:00:00 1970 +++ debugger-merge/gdbproxy.c Tue Jul 9 21:50:59 2002 @@ -0,0 +1,1949 @@ +/* + * A Win32 based proxy implementing the GBD remote protocol + * This allows to debug Wine (and any "emulated" program) under + * Linux using GDB + * + * (c) Eric Pouech 2002 + */ + +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/poll.h> +#include <sys/wait.h> +#ifdef HAVE_SYS_SOCKET_H +# include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <unistd.h> + +#include "windef.h" +#include "winbase.h" +#include "tlhelp32.h" + +/* those two are needed only for the SHOWNORMAL flag */ +#include "wingdi.h" +#include "winuser.h" + +#include "debugger.h" + +#define GDBPXY_TRC_LOWLEVEL 0x01 +#define GDBPXY_TRC_PACKET 0x02 +#define GDBPXY_TRC_COMMAND 0x04 +#define GDBPXY_TRC_COMMAND_ERROR 0x08 +#define GDBPXY_TRC_WIN32_EVENT 0x10 +#define GDBPXY_TRC_WIN32_ERROR 0x20 + +struct gdb_ctx_Xpoint +{ + int type; /* -1 means free */ + void* addr; + unsigned long val; +}; + +struct gdb_context +{ + /* gdb information */ + int sock; + /* incoming buffer */ + char* in_buf; + int in_buf_alloc; + int in_len; + /* split into individual packet */ + char* in_packet; + int in_packet_len; + /* outgoing buffer */ + char* out_buf; + int out_buf_alloc; + int out_len; + int out_curr_packet; + /* generic GDB thread information */ + unsigned exec_thread; /* thread used in step & continue */ + unsigned other_thread; /* thread to be used in any other operation */ + unsigned trace; + /* current Win32 trap env */ + unsigned last_sig; + BOOL in_trap; + CONTEXT context; + /* Win32 information */ + DBG_PROCESS* process; +#define NUM_XPOINT 32 + struct gdb_ctx_Xpoint Xpoints[NUM_XPOINT]; + /* Unix environment */ + unsigned long wine_segs[3]; /* load addresses of the ELF wine exec segments (text, bss and data) */ +}; + +/* =============================================== * + * B A S I C M A N I P U L A T I O N S * + * =============================================== * + */ + +static inline int hex_from0(char ch) +{ + if (ch >= '0' && ch <= '9') return ch - '0'; + if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; + if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; + assert(0); +} + +static inline unsigned char hex_to0(int x) +{ + assert(x >= 0 && x < 16); + return "0123456789abcdef"[x]; +} + +static void hex_from(void* dst, const char* src, size_t len) +{ + while (len--) + { + *(unsigned char*)dst++ = (hex_from0(src[0]) << 4) | hex_from0(src[1]); + src += 2; + } +} + +static void hex_to(char* dst, const void* src, size_t len) +{ + while (len--) + { + *dst++ = hex_to0(*(const unsigned char*)src >> 4); + *dst++ = hex_to0(*(const unsigned char*)src & 0x0F); + src++; + } +} + +static unsigned char checksum(const char* ptr, int len) +{ + unsigned cksum = 0; + + while (len-- > 0) + cksum += (unsigned char)*ptr++; + return cksum; +} + +/* =============================================== * + * C P U H A N D L E R S * + * =============================================== * + */ + +#define OFFSET_OF(__c,__f) ((int)(((char*)&(((__c*)0)->__f))-((char*)0))) + +#ifdef __i386__ +static size_t cpu_register_map[] = { + OFFSET_OF(CONTEXT, Eax), + OFFSET_OF(CONTEXT, Ecx), + OFFSET_OF(CONTEXT, Edx), + OFFSET_OF(CONTEXT, Ebx), + OFFSET_OF(CONTEXT, Esp), + OFFSET_OF(CONTEXT, Ebp), + OFFSET_OF(CONTEXT, Esi), + OFFSET_OF(CONTEXT, Edi), + OFFSET_OF(CONTEXT, Eip), + OFFSET_OF(CONTEXT, EFlags), + OFFSET_OF(CONTEXT, SegCs), + OFFSET_OF(CONTEXT, SegSs), + OFFSET_OF(CONTEXT, SegDs), + OFFSET_OF(CONTEXT, SegEs), + OFFSET_OF(CONTEXT, SegFs), + OFFSET_OF(CONTEXT, SegGs), +}; +#else +#error "Define the registers map for your CPU" +#endif +#undef OFFSET_OF + +static const size_t cpu_num_regs = (sizeof(cpu_register_map) / sizeof(cpu_register_map[0])); + +static inline unsigned long* cpu_register(struct gdb_context* gdbctx, unsigned idx) +{ + assert(idx < cpu_num_regs); + return (unsigned long*)((char*)&gdbctx->context + cpu_register_map[idx]); +} + +static inline BOOL cpu_enter_stepping(struct gdb_context* gdbctx) +{ +#ifdef __i386__ + gdbctx->context.EFlags |= 0x100; + return TRUE; +#else +#error "Define step mode enter for your CPU" +#endif + return FALSE; +} + +static inline BOOL cpu_leave_stepping(struct gdb_context* gdbctx) +{ +#ifdef __i386__ + /* The Win32 debug API always resets the Step bit in EFlags after + * a single step instruction, so we don't need to clear when the + * step is done. + */ + return TRUE; +#else +#error "Define step mode leave for your CPU" +#endif + return FALSE; +} + +#ifdef __i386__ +#define DR7_CONTROL_SHIFT 16 +#define DR7_CONTROL_SIZE 4 + +#define DR7_RW_EXECUTE (0x0) +#define DR7_RW_WRITE (0x1) +#define DR7_RW_READ (0x3) + +#define DR7_LEN_1 (0x0) +#define DR7_LEN_2 (0x4) +#define DR7_LEN_4 (0xC) + +#define DR7_LOCAL_ENABLE_SHIFT 0 +#define DR7_GLOBAL_ENABLE_SHIFT 1 +#define DR7_ENABLE_SIZE 2 + +#define DR7_LOCAL_ENABLE_MASK (0x55) +#define DR7_GLOBAL_ENABLE_MASK (0xAA) + +#define DR7_CONTROL_RESERVED (0xFC00) +#define DR7_LOCAL_SLOWDOWN (0x100) +#define DR7_GLOBAL_SLOWDOWN (0x200) + +#define DR7_ENABLE_MASK(dr) (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr))) +#define IS_DR7_SET(ctrl,dr) ((ctrl)&DR7_ENABLE_MASK(dr)) + +static inline int i386_get_unused_DR(struct gdb_context* gdbctx, + unsigned long** r) +{ + if (!IS_DR7_SET(gdbctx->context.Dr7, 0)) + { + *r = &gdbctx->context.Dr0; + return 0; + } + if (!IS_DR7_SET(gdbctx->context.Dr7, 1)) + { + *r = &gdbctx->context.Dr1; + return 1; + } + if (!IS_DR7_SET(gdbctx->context.Dr7, 2)) + { + *r = &gdbctx->context.Dr2; + return 2; + } + if (!IS_DR7_SET(gdbctx->context.Dr7, 3)) + { + *r = &gdbctx->context.Dr3; + return 3; + } + return -1; +} +#endif + +/****************************************************************** + * cpu_insert_Xpoint + * + * returns 1 if ok + * 0 if error + * -1 if operation isn't supported by CPU + */ +static inline int cpu_insert_Xpoint(struct gdb_context* gdbctx, + struct gdb_ctx_Xpoint* xpt, size_t len) +{ +#ifdef __i386__ + unsigned char ch; + unsigned long sz; + unsigned long* pr; + int reg; + unsigned long bits; + + switch (xpt->type) + { + case '0': + if (len != 1) return 0; + if (!ReadProcessMemory(gdbctx->process->handle, xpt->addr, &ch, 1, &sz) || sz != 1) return 0; + xpt->val = ch; + ch = 0xcc; + if (!WriteProcessMemory(gdbctx->process->handle, xpt->addr, &ch, 1, &sz) || sz != 1) return 0; + break; + case '1': + bits = DR7_RW_EXECUTE; + goto hw_bp; + case '2': + bits = DR7_RW_READ; + goto hw_bp; + case '3': + bits = DR7_RW_WRITE; + hw_bp: + if ((reg = i386_get_unused_DR(gdbctx, &pr)) == -1) return 0; + *pr = (unsigned long)xpt->addr; + if (xpt->type != '1') switch (len) + { + case 4: bits |= DR7_LEN_4; break; + case 2: bits |= DR7_LEN_2; break; + case 1: bits |= DR7_LEN_1; break; + default: return 0; + } + xpt->val = reg; + /* clear old values */ + gdbctx->context.Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg)); + /* set the correct ones */ + gdbctx->context.Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg); + gdbctx->context.Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN; + break; + default: + fprintf(stderr, "Unknown bp type %c\n", xpt->type); + return 0; + } + return 1; +#else +#error "Define insert Xpoint for your CPU" +#endif + return -1; +} + +/****************************************************************** + * cpu_remove_Xpoint + * + * returns 1 if ok + * 0 if error + * -1 if operation isn't supported by CPU + */ +static inline BOOL cpu_remove_Xpoint(struct gdb_context* gdbctx, + struct gdb_ctx_Xpoint* xpt, size_t len) +{ +#ifdef __i386__ + unsigned long sz; + unsigned char ch; + + switch (xpt->type) + { + case '0': + if (len != 1) return 0; + ch = (unsigned char)xpt->val; + if (!WriteProcessMemory(gdbctx->process->handle, xpt->addr, &ch, 1, &sz) || sz != 1) return 0; + break; + case '1': + case '2': + case '3': + /* simply disable the entry */ + gdbctx->context.Dr7 &= ~DR7_ENABLE_MASK(xpt->val); + break; + default: + fprintf(stderr, "Unknown bp type %c\n", xpt->type); + return 0; + } + return 1; +#else +#error "Define remove Xpoint for your CPU" +#endif + return -1; +} +/* =============================================== * + * W I N 3 2 D E B U G I N T E R F A C E * + * =============================================== * + */ + +static BOOL handle_exception(struct gdb_context* gdbctx, EXCEPTION_DEBUG_INFO* exc) +{ + EXCEPTION_RECORD* rec = &exc->ExceptionRecord; + BOOL ret = FALSE; + + switch (rec->ExceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_PRIV_INSTRUCTION: + case EXCEPTION_STACK_OVERFLOW: + case EXCEPTION_GUARD_PAGE: + gdbctx->last_sig = SIGSEGV; + ret = TRUE; + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + gdbctx->last_sig = SIGBUS; + ret = TRUE; + break; + case EXCEPTION_SINGLE_STEP: + /* fall thru */ + case EXCEPTION_BREAKPOINT: + gdbctx->last_sig = SIGTRAP; + ret = TRUE; + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + gdbctx->last_sig = SIGFPE; + ret = TRUE; + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + gdbctx->last_sig = SIGFPE; + ret = TRUE; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + gdbctx->last_sig = SIGILL; + ret = TRUE; + break; + case CONTROL_C_EXIT: + gdbctx->last_sig = SIGINT; + ret = TRUE; + break; + case EXCEPTION_CRITICAL_SECTION_WAIT: + gdbctx->last_sig = SIGALRM; + ret = TRUE; + /* FIXME: we could also add here a O packet with additional information */ + break; + default: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "unhandled exception code %08lx\n", rec->ExceptionCode); + gdbctx->last_sig = SIGABRT; + ret = TRUE; + break; + } + return ret; +} + +static void handle_debug_event(struct gdb_context* gdbctx, DEBUG_EVENT* de) +{ + char buffer[256]; + + DEBUG_CurrThread = DEBUG_GetThread(gdbctx->process, de->dwThreadId); + + switch (de->dwDebugEventCode) + { + case CREATE_PROCESS_DEBUG_EVENT: + DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer), + de->u.CreateProcessInfo.hProcess, + de->u.CreateProcessInfo.lpImageName); + + /* FIXME unicode ? de->u.CreateProcessInfo.fUnicode */ + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: create process '%s'/%p @%08lx (%ld<%ld>)\n", + de->dwProcessId, de->dwThreadId, + buffer, de->u.CreateProcessInfo.lpImageName, + (unsigned long)(LPVOID)de->u.CreateProcessInfo.lpStartAddress, + de->u.CreateProcessInfo.dwDebugInfoFileOffset, + de->u.CreateProcessInfo.nDebugInfoSize); + + gdbctx->process = DEBUG_AddProcess(de->dwProcessId, + de->u.CreateProcessInfo.hProcess, + buffer); + /* de->u.CreateProcessInfo.lpStartAddress; */ + + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: create thread I @%08lx\n", + de->dwProcessId, de->dwThreadId, + (unsigned long)(LPVOID)de->u.CreateProcessInfo.lpStartAddress); + + assert(DEBUG_CurrThread == NULL); /* shouldn't be there */ + DEBUG_AddThread(gdbctx->process, de->dwThreadId, + de->u.CreateProcessInfo.hThread, + de->u.CreateProcessInfo.lpStartAddress, + de->u.CreateProcessInfo.lpThreadLocalBase); +#if 0 + DEBUG_LoadModule32(DEBUG_CurrProcess->imageName, de->u.CreateProcessInfo.hFile, + (DWORD)de->u.CreateProcessInfo.lpBaseOfImage); + + if (buffer[0]) /* we got a process name */ + { + DWORD type; + if (!GetBinaryTypeA( buffer, &type )) + { + /* not a Windows binary, assume it's a Unix executable then */ + char unixname[MAX_PATH]; + /* HACK!! should fix DEBUG_ReadExecutableDbgInfo to accept DOS filenames */ + if (wine_get_unix_file_name( buffer, unixname, sizeof(unixname) )) + { + DEBUG_ReadExecutableDbgInfo( unixname ); + break; + } + } + } + /* if it is a Windows binary, or an invalid or missing file name, + * we use wine itself as the main executable */ + DEBUG_ReadExecutableDbgInfo( "wine" ); +#endif + break; + + case LOAD_DLL_DEBUG_EVENT: + assert(DEBUG_CurrThread); + DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer), + gdbctx->process->handle, + de->u.LoadDll.lpImageName); + + /* FIXME unicode: de->u.LoadDll.fUnicode */ + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: loads DLL %s @%08lx (%ld<%ld>)\n", + de->dwProcessId, de->dwThreadId, + buffer, (unsigned long)de->u.LoadDll.lpBaseOfDll, + de->u.LoadDll.dwDebugInfoFileOffset, + de->u.LoadDll.nDebugInfoSize); +#if 0 + _strupr(buffer); + DEBUG_LoadModule32(buffer, de->u.LoadDll.hFile, (DWORD)de->u.LoadDll.lpBaseOfDll); + DEBUG_CheckDelayedBP(); + if (DBG_IVAR(BreakOnDllLoad)) + { + DEBUG_Printf(DBG_CHN_MESG, "Stopping on DLL %s loading at %08lx\n", + buffer, (unsigned long)de->u.LoadDll.lpBaseOfDll); + DEBUG_Parser(); + } +#endif + break; + + case UNLOAD_DLL_DEBUG_EVENT: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: unload DLL @%08lx\n", + de->dwProcessId, de->dwThreadId, (unsigned long)de->u.UnloadDll.lpBaseOfDll); + break; + + case EXCEPTION_DEBUG_EVENT: + assert(DEBUG_CurrThread); + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: exception code=%08lx\n", + de->dwProcessId, de->dwThreadId, + de->u.Exception.ExceptionRecord.ExceptionCode); + + gdbctx->context.ContextFlags = CONTEXT_CONTROL + | CONTEXT_INTEGER +#ifdef CONTEXT_SEGMENTS + | CONTEXT_SEGMENTS +#endif +#ifdef CONTEXT_DEBUG_REGISTERS + | CONTEXT_DEBUG_REGISTERS +#endif + ; + if (!GetThreadContext(DEBUG_CurrThread->handle, &gdbctx->context)) + { + if (gdbctx->trace & GDBPXY_TRC_WIN32_ERROR) + fprintf(stderr, "Can't get thread's context\n"); + break; + } + gdbctx->in_trap = handle_exception(gdbctx, &de->u.Exception); + break; + + case CREATE_THREAD_DEBUG_EVENT: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: create thread D @%08lx\n", + de->dwProcessId, de->dwThreadId, (unsigned long)(LPVOID)de->u.CreateThread.lpStartAddress); + + DEBUG_AddThread(gdbctx->process, + de->dwThreadId, + de->u.CreateThread.hThread, + de->u.CreateThread.lpStartAddress, + de->u.CreateThread.lpThreadLocalBase); + break; + + case EXIT_THREAD_DEBUG_EVENT: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: exit thread (%ld)\n", + de->dwProcessId, de->dwThreadId, de->u.ExitThread.dwExitCode); + + assert(DEBUG_CurrThread); + DEBUG_DelThread(DEBUG_CurrThread); + break; + + case EXIT_PROCESS_DEBUG_EVENT: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: exit process (%ld)\n", + de->dwProcessId, de->dwThreadId, de->u.ExitProcess.dwExitCode); + + DEBUG_DelProcess(gdbctx->process); + gdbctx->process = NULL; + /* now signal gdb that we're done */ + gdbctx->last_sig = SIGTERM; + gdbctx->in_trap = TRUE; + break; + + case OUTPUT_DEBUG_STRING_EVENT: + assert(DEBUG_CurrThread); + DEBUG_ProcessGetString(buffer, sizeof(buffer), + gdbctx->process->handle, + de->u.DebugString.lpDebugStringData); + /* FIXME unicode de->u.DebugString.fUnicode ? */ + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: output debug string (%s)\n", + de->dwProcessId, de->dwThreadId, buffer); + break; + + case RIP_EVENT: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: rip error=%ld type=%ld\n", + de->dwProcessId, de->dwThreadId, de->u.RipInfo.dwError, + de->u.RipInfo.dwType); + break; + + default: + if (gdbctx->trace & GDBPXY_TRC_WIN32_EVENT) + fprintf(stderr, "%08lx:%08lx: unknown event (%ld)\n", + de->dwProcessId, de->dwThreadId, de->dwDebugEventCode); + } +} + +static void resume_debuggee(struct gdb_context* gdbctx, unsigned long cont) +{ + if (DEBUG_CurrThread) + { + if (!SetThreadContext(DEBUG_CurrThread->handle, &gdbctx->context)) + if (gdbctx->trace & GDBPXY_TRC_WIN32_ERROR) + fprintf(stderr, "cannot set ctx on %lu\n", DEBUG_CurrThread->tid); + if (!ContinueDebugEvent(gdbctx->process->pid, DEBUG_CurrThread->tid, cont)) + if (gdbctx->trace & GDBPXY_TRC_WIN32_ERROR) + fprintf(stderr, "cannot continue on %lu (%lu)\n", + DEBUG_CurrThread->tid, cont); + } + else if (gdbctx->trace & GDBPXY_TRC_WIN32_ERROR) + fprintf(stderr, "cannot find last thread (%lu)\n", DEBUG_CurrThread->tid); +} + +static void wait_for_debuggee(struct gdb_context* gdbctx) +{ + DEBUG_EVENT de; + + gdbctx->in_trap = FALSE; + while (WaitForDebugEvent(&de, INFINITE)) + { + handle_debug_event(gdbctx, &de); + assert(!gdbctx->process || + gdbctx->process->pid == 0 || + de.dwProcessId == gdbctx->process->pid); + assert(!DEBUG_CurrThread || de.dwThreadId == DEBUG_CurrThread->tid); + if (gdbctx->in_trap) break; + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); + } +} + +static void detach_debuggee(struct gdb_context* gdbctx, BOOL kill) +{ + cpu_leave_stepping(gdbctx); + resume_debuggee(gdbctx, DBG_CONTINUE); + if (!kill) + DebugActiveProcessStop(gdbctx->process->pid); + DEBUG_DelProcess(gdbctx->process); + gdbctx->process = NULL; +} + +static void get_process_info(struct gdb_context* gdbctx, char* buffer, size_t len) +{ + unsigned long status; + + if (!GetExitCodeProcess(gdbctx->process->handle, &status)) + { + strcpy(buffer, "Unknown process"); + return; + } + if (status == STILL_ACTIVE) + { + strcpy(buffer, "Running"); + } + else + sprintf(buffer, "Terminated (%lu)", status); + + switch (GetPriorityClass(gdbctx->process->handle)) + { + case 0: break; +#ifdef ABOVE_NORMAL_PRIORITY_CLASS + case ABOVE_NORMAL_PRIORITY_CLASS: strcat(buffer, ", above normal priority"); break; +#endif +#ifdef BELOW_NORMAL_PRIORITY_CLASS + case BELOW_NORMAL_PRIORITY_CLASS: strcat(buffer, ", below normal priotity"); break; +#endif + case HIGH_PRIORITY_CLASS: strcat(buffer, ", high priority"); break; + case IDLE_PRIORITY_CLASS: strcat(buffer, ", idle priority"); break; + case NORMAL_PRIORITY_CLASS: strcat(buffer, ", normal priority"); break; + case REALTIME_PRIORITY_CLASS: strcat(buffer, ", realtime priority"); break; + } + strcat(buffer, "\n"); +} + +static void get_thread_info(struct gdb_context* gdbctx, unsigned tid, + char* buffer, size_t len) +{ + DBG_THREAD* thd; + unsigned long status; + int prio; + + /* FIXME: use the size of buffer */ + thd = DEBUG_GetThread(gdbctx->process, tid); + if (thd == NULL) + { + strcpy(buffer, "No information"); + return; + } + if (GetExitCodeThread(thd->handle, &status)) + { + if (status == STILL_ACTIVE) + { + /* FIXME: this is a bit brutal... some nicer way shall be found */ + switch (status = SuspendThread(thd->handle)) + { + case -1: break; + case 0: strcpy(buffer, "Running"); break; + default: sprintf(buffer, "Suspended (%lu)", status - 1); + } + ResumeThread(thd->handle); + } + else + sprintf(buffer, "Terminated (exit code = %lu)", status); + } + else + { + strcpy(buffer, "Unknown threadID"); + } + switch (prio = GetThreadPriority(thd->handle)) + { + case THREAD_PRIORITY_ERROR_RETURN: break; + case THREAD_PRIORITY_ABOVE_NORMAL: strcat(buffer, ", priority +1 above normal"); break; + case THREAD_PRIORITY_BELOW_NORMAL: strcat(buffer, ", priority -1 below normal"); break; + case THREAD_PRIORITY_HIGHEST: strcat(buffer, ", priority +2 above normal"); break; + case THREAD_PRIORITY_LOWEST: strcat(buffer, ", priority -2 below normal"); break; + case THREAD_PRIORITY_IDLE: strcat(buffer, ", priority idle"); break; + case THREAD_PRIORITY_NORMAL: strcat(buffer, ", priority normal"); break; + case THREAD_PRIORITY_TIME_CRITICAL: strcat(buffer, ", priority time-critical"); break; + default: sprintf(buffer + strlen(buffer), ", priority = %d", prio); + } + assert(strlen(buffer) < len); +} + +/* =============================================== * + * P A C K E T U T I L S * + * =============================================== * + */ + +enum packet_return {packet_error = 0x00, packet_ok = 0x01, packet_done = 0x02, + packet_last_f = 0x80}; + +static void packet_reply_grow(struct gdb_context* gdbctx, size_t size) +{ + if (gdbctx->out_buf_alloc < gdbctx->out_len + size) + { + gdbctx->out_buf_alloc = ((gdbctx->out_len + size) / 32 + 1) * 32; + gdbctx->out_buf = realloc(gdbctx->out_buf, gdbctx->out_buf_alloc); + } +} + +static void packet_reply_hex_to(struct gdb_context* gdbctx, const void* src, int len) +{ + packet_reply_grow(gdbctx, len * 2); + hex_to(&gdbctx->out_buf[gdbctx->out_len], src, len); + gdbctx->out_len += len * 2; +} + +static void packet_reply_val(struct gdb_context* gdbctx, unsigned long val, int len) +{ + int i, shift; + + shift = (len - 1) * 8; + packet_reply_grow(gdbctx, len * 2); + for (i = 0; i < len; i++, shift -= 8) + { + gdbctx->out_buf[gdbctx->out_len++] = hex_to0((val >> (shift + 4)) & 0x0F); + gdbctx->out_buf[gdbctx->out_len++] = hex_to0((val >> shift ) & 0x0F); + } +} + +static inline void packet_reply_add(struct gdb_context* gdbctx, const char* str, int len) +{ + packet_reply_grow(gdbctx, len); + memcpy(&gdbctx->out_buf[gdbctx->out_len], str, len); + gdbctx->out_len += len; +} + +static inline void packet_reply_cat(struct gdb_context* gdbctx, const char* str) +{ + packet_reply_add(gdbctx, str, strlen(str)); +} + +static inline void packet_reply_catc(struct gdb_context* gdbctx, char ch) +{ + packet_reply_add(gdbctx, &ch, 1); +} + +static void packet_reply_open(struct gdb_context* gdbctx) +{ + assert(gdbctx->out_curr_packet == -1); + packet_reply_catc(gdbctx, '$'); + gdbctx->out_curr_packet = gdbctx->out_len; +} + +static void packet_reply_close(struct gdb_context* gdbctx) +{ + unsigned char cksum; + unsigned plen; + + plen = gdbctx->out_len - gdbctx->out_curr_packet; + packet_reply_catc(gdbctx, '#'); + cksum = checksum(&gdbctx->out_buf[gdbctx->out_curr_packet], plen); + packet_reply_hex_to(gdbctx, &cksum, 1); + if (gdbctx->trace & GDBPXY_TRC_PACKET) + fprintf(stderr, "Reply : %*.*s\n", + plen, plen, &gdbctx->out_buf[gdbctx->out_curr_packet]); + gdbctx->out_curr_packet = -1; +} + +static enum packet_return packet_reply(struct gdb_context* gdbctx, const char* packet, int len) +{ + packet_reply_open(gdbctx); + + if (len == -1) len = strlen(packet); + assert(memchr(packet, '$', len) == NULL && memchr(packet, '#', len) == NULL); + + packet_reply_add(gdbctx, packet, len); + + packet_reply_close(gdbctx); + + return packet_done; +} + +static enum packet_return packet_reply_error(struct gdb_context* gdbctx, int error) +{ + packet_reply_open(gdbctx); + + packet_reply_add(gdbctx, "E", 1); + packet_reply_val(gdbctx, error, 1); + + packet_reply_close(gdbctx); + + return packet_done; +} + +/* =============================================== * + * P A C K E T H A N D L E R S * + * =============================================== * + */ + +static enum packet_return packet_reply_status(struct gdb_context* gdbctx) +{ + enum packet_return ret = packet_done; + + packet_reply_open(gdbctx); + + if (gdbctx->process != NULL) + { + unsigned char sig; + unsigned i; + + packet_reply_catc(gdbctx, 'T'); + sig = gdbctx->last_sig; + packet_reply_val(gdbctx, sig, 1); + packet_reply_add(gdbctx, "thread:", 7); + packet_reply_val(gdbctx, DEBUG_CurrThread->tid, 4); + packet_reply_catc(gdbctx, ';'); + + for (i = 0; i < cpu_num_regs; i++) + { + /* FIXME: this call will also grow the buffer... + * unneeded, but not harmful + */ + packet_reply_val(gdbctx, i, 1); + packet_reply_catc(gdbctx, ':'); + packet_reply_hex_to(gdbctx, cpu_register(gdbctx, i), 4); + packet_reply_catc(gdbctx, ';'); + } + } + else + { + /* Try to put an exit code + * Cannot use GetExitCodeProcess, wouldn't fit in a 8 bit value, so + * just indicate the end of process and exit */ + packet_reply_add(gdbctx, "W00", 3); + /*if (!gdbctx->extended)*/ ret |= packet_last_f; + } + + packet_reply_close(gdbctx); + + return ret; +} + +#if 0 +static enum packet_return packet_extended(struct gdb_context* gdbctx) +{ + gdbctx->extended = 1; + return packet_ok; +} +#endif + +static enum packet_return packet_last_signal(struct gdb_context* gdbctx) +{ + assert(gdbctx->in_packet_len == 0); + return packet_reply_status(gdbctx); +} + +static enum packet_return packet_continue(struct gdb_context* gdbctx) +{ + /* FIXME: add support for address in packet */ + assert(gdbctx->in_packet_len == 0); + if (DEBUG_CurrThread->tid != gdbctx->exec_thread && gdbctx->exec_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: cont on %u, while last thd is %lu\n", + gdbctx->exec_thread, DEBUG_CurrThread->tid); + resume_debuggee(gdbctx, DBG_CONTINUE); + wait_for_debuggee(gdbctx); + return packet_reply_status(gdbctx); +} + +static enum packet_return packet_continue_signal(struct gdb_context* gdbctx) +{ + unsigned char sig; + + /* FIXME: add support for address in packet */ + assert(gdbctx->in_packet_len == 2); + if (DEBUG_CurrThread->tid != gdbctx->exec_thread && gdbctx->exec_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: cont/sig on %u, while last thd is %lu\n", + gdbctx->exec_thread, DEBUG_CurrThread->tid); + hex_from(&sig, gdbctx->in_packet, 1); + /* cannot change signals on the fly */ + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "sigs: %u %u\n", sig, gdbctx->last_sig); + if (sig != gdbctx->last_sig) + return packet_error; + resume_debuggee(gdbctx, DBG_EXCEPTION_NOT_HANDLED); + wait_for_debuggee(gdbctx); + return packet_reply_status(gdbctx); +} + +static enum packet_return packet_detach(struct gdb_context* gdbctx) +{ + detach_debuggee(gdbctx, FALSE); + return packet_ok | packet_last_f; +} + +static enum packet_return packet_read_registers(struct gdb_context* gdbctx) +{ + int i; + + assert(gdbctx->in_trap); + if (DEBUG_CurrThread->tid != gdbctx->other_thread && gdbctx->other_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: read regs on %u, while last thd is %lu\n", + gdbctx->other_thread, DEBUG_CurrThread->tid); + + packet_reply_open(gdbctx); + + for (i = 0; i < cpu_num_regs; i++) + { + packet_reply_hex_to(gdbctx, cpu_register(gdbctx, i), 4); + } + + packet_reply_close(gdbctx); + return packet_done; +} + +static enum packet_return packet_write_registers(struct gdb_context* gdbctx) +{ + unsigned i; + + assert(gdbctx->in_trap); + if (DEBUG_CurrThread->tid != gdbctx->other_thread && gdbctx->other_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: write regs on %u, while last thd is %lu\n", + gdbctx->other_thread, DEBUG_CurrThread->tid); + if (gdbctx->in_packet_len < cpu_num_regs * 2) return packet_error; + for (i = 0; i < cpu_num_regs; i++) + hex_from(cpu_register(gdbctx, i), &gdbctx->in_packet[8 * i], 4); + return packet_ok; +} + +static enum packet_return packet_kill(struct gdb_context* gdbctx) +{ + detach_debuggee(gdbctx, TRUE); +#if 0 + if (!gdbctx->extended) + /* dunno whether GDB cares or not */ +#endif + wait(NULL); + exit(0); + /* assume we can't really answer something here */ + /* return packet_done; */ +} + +static enum packet_return packet_thread(struct gdb_context* gdbctx) +{ + char* end; + unsigned thread; + + switch (gdbctx->in_packet[0]) + { + case 'c': + case 'g': + if (gdbctx->in_packet[1] == '-') + thread = -strtol(gdbctx->in_packet + 2, &end, 16); + else + thread = strtol(gdbctx->in_packet + 1, &end, 16); + if (end == NULL || end > gdbctx->in_packet + gdbctx->in_packet_len) + { + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "Cannot get threadid %*.*s\n", + gdbctx->in_packet_len - 1, gdbctx->in_packet_len - 1, + gdbctx->in_packet + 1); + return packet_error; + } + if (gdbctx->in_packet[0] == 'c') + gdbctx->exec_thread = thread; + else + gdbctx->other_thread = thread; + return packet_ok; + default: + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "Unknown thread sub-command %c\n", gdbctx->in_packet[0]); + return packet_error; + } +} + +static enum packet_return packet_read_memory(struct gdb_context* gdbctx) +{ + const char* addr; + size_t len, blk_len, nread; + char buffer[32]; + unsigned long r = 0; + + assert(gdbctx->in_trap); + /* FIXME:check in_packet_len for reading %p,%x */ + if (sscanf(gdbctx->in_packet, "%p,%x", &addr, &len) != 2) return packet_error; + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "read mem at %p for %u bytes\n", addr, len); + for (nread = 0; nread < len > 0; nread += r, addr += r) + { + blk_len = min(sizeof(buffer), len - nread); + if (!ReadProcessMemory(gdbctx->process->handle, addr, buffer, blk_len, &r) || + r == 0) + { + /* fail at first address, return error */ + if (nread == 0) return packet_reply_error(gdbctx, EFAULT); + /* something has already been read, return partial information */ + break; + } + if (nread == 0) packet_reply_open(gdbctx); + packet_reply_hex_to(gdbctx, buffer, r); + } + packet_reply_close(gdbctx); + return packet_done; +} + +static enum packet_return packet_write_memory(struct gdb_context* gdbctx) +{ + char* addr; + size_t len, blk_len; + char* ptr; + char buffer[32]; + unsigned long w; + + assert(gdbctx->in_trap); + ptr = memchr(gdbctx->in_packet, ':', gdbctx->in_packet_len); + if (ptr == NULL) + { + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "cannot find ':' in %*.*s\n", + gdbctx->in_packet_len, gdbctx->in_packet_len, gdbctx->in_packet); + return packet_error; + } + *ptr++ = '\0'; + + if (sscanf(gdbctx->in_packet, "%p,%x", &addr, &len) != 2) + { + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "cannot scan addr,len in %s\n", gdbctx->in_packet); + return packet_error; + } + if (ptr - gdbctx->in_packet + len * 2 != gdbctx->in_packet_len) + { + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "wrong sizes %u <> %u\n", + ptr - gdbctx->in_packet + len * 2, gdbctx->in_packet_len); + return packet_error; + } + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "write %u bytes at %p\n", len, addr); + while (len > 0) + { + blk_len = min(sizeof(buffer), len); + hex_from(buffer, ptr, blk_len); + { + BOOL ret; + + ret = WriteProcessMemory(gdbctx->process->handle, addr, buffer, blk_len, &w); + if (!ret || w != blk_len) + break; + } + addr += w; + len -= w; + ptr += w; + } + return packet_ok; /* FIXME: error while writing ? */ +} + +static enum packet_return packet_write_register(struct gdb_context* gdbctx) +{ + unsigned reg; + char* ptr; + char* end; + + assert(gdbctx->in_trap); + if (DEBUG_CurrThread->tid != gdbctx->other_thread && gdbctx->other_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: read reg on %u, while last thd is %lu\n", + gdbctx->other_thread, DEBUG_CurrThread->tid); + + ptr = memchr(gdbctx->in_packet, '=', gdbctx->in_packet_len); + *ptr++ = '\0'; + reg = strtoul(gdbctx->in_packet, &end, 16); + if (end == NULL || reg > cpu_num_regs) + { + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "invalid register index %s\n", gdbctx->in_packet); + /* FIXME: if just the reg is above cpu_num_regs, don't tell gdb + * it wouldn't matter too much, and it fakes our support for all regs + */ + return (end == NULL) ? packet_error : packet_ok; + } + if (ptr + 8 - gdbctx->in_packet != gdbctx->in_packet_len) + { + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "wrong sizes %u <> %u\n", + ptr + 8 - gdbctx->in_packet, gdbctx->in_packet_len); + return packet_error; + } + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "Writing reg %u <= %*.*s\n", + reg, gdbctx->in_packet_len - (ptr - gdbctx->in_packet), + gdbctx->in_packet_len - (ptr - gdbctx->in_packet), ptr); + hex_from(cpu_register(gdbctx, reg), ptr, 4); + return packet_ok; +} + +static void packet_query_monitor_wnd_helper(struct gdb_context* gdbctx, HWND hWnd, int indent) +{ + char buffer[128]; + char clsName[128]; + char wndName[128]; + HWND child; + + do { + if (!GetClassName(hWnd, clsName, sizeof(clsName))) + strcpy(clsName, "-- Unknown --"); + if (!GetWindowText(hWnd, wndName, sizeof(wndName))) + strcpy(wndName, "-- Empty --"); + + packet_reply_open(gdbctx); + packet_reply_catc(gdbctx, 'O'); + sprintf(buffer, "%*s%04x%*s%-17.17s %08lx %08lx %.14s\n", + indent, "", (UINT)hWnd, 13 - indent, "", + clsName, GetWindowLong(hWnd, GWL_STYLE), + GetWindowLong(hWnd, GWL_WNDPROC), wndName); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); + + if ((child = GetWindow(hWnd, GW_CHILD)) != 0) + packet_query_monitor_wnd_helper(gdbctx, child, indent + 1); + } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0); +} + +static void packet_query_monitor_wnd(struct gdb_context* gdbctx, int len, const char* str) +{ + char buffer[128]; + + /* we do the output in several 'O' packets, with the last one being just OK for + * marking the end of the output */ + packet_reply_open(gdbctx); + packet_reply_catc(gdbctx, 'O'); + sprintf(buffer, "%-16.16s %-17.17s %-8.8s %s\n", + "hwnd", "Class Name", " Style", " WndProc Text"); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); + + /* FIXME: could also add a pmt to this command in str... */ + packet_query_monitor_wnd_helper(gdbctx, GetDesktopWindow(), 0); + packet_reply(gdbctx, "OK", 2); +} + +static void packet_query_monitor_process(struct gdb_context* gdbctx, int len, const char* str) +{ + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + char buffer[128]; + char deco; + PROCESSENTRY32 entry; + BOOL ok; + + if (snap == INVALID_HANDLE_VALUE) + return; + + entry.dwSize = sizeof(entry); + ok = Process32First( snap, &entry ); + + /* we do the output in several 'O' packets, with the last one being just OK for + * marking the end of the output */ + + packet_reply_open(gdbctx); + packet_reply_catc(gdbctx, 'O'); + sprintf(buffer, " %-8.8s %-8.8s %-8.8s %s\n", + "pid", "threads", "parent", "executable" ); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); + + while (ok) + { + deco = ' '; + if (entry.th32ProcessID == gdbctx->process->pid) deco = '>'; + packet_reply_open(gdbctx); + packet_reply_catc(gdbctx, 'O'); + sprintf(buffer, "%c%08lx %-8ld %08lx '%s'\n", + deco, entry.th32ProcessID, entry.cntThreads, + entry.th32ParentProcessID, entry.szExeFile); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); + ok = Process32Next(snap, &entry); + } + CloseHandle(snap); + packet_reply(gdbctx, "OK", 2); +} + +static void packet_query_monitor_mem(struct gdb_context* gdbctx, int len, const char* str) +{ + MEMORY_BASIC_INFORMATION mbi; + char* addr = 0; + char* state; + char* type; + char prot[3+1]; + char buffer[128]; + + /* we do the output in several 'O' packets, with the last one being just OK for + * marking the end of the output */ + packet_reply_open(gdbctx); + packet_reply_catc(gdbctx, 'O'); + sprintf(buffer, "Address Size State Type RWX\n"); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); + + while (VirtualQueryEx(gdbctx->process->handle, addr, &mbi, sizeof(mbi)) >= sizeof(mbi)) + { + switch (mbi.State) + { + case MEM_COMMIT: state = "commit "; break; + case MEM_FREE: state = "free "; break; + case MEM_RESERVE: state = "reserve"; break; + default: state = "??? "; break; + } + if (mbi.State != MEM_FREE) + { + switch (mbi.Type) + { + case MEM_IMAGE: type = "image "; break; + case MEM_MAPPED: type = "mapped "; break; + case MEM_PRIVATE: type = "private"; break; + case 0: type = " "; break; + default: type = "??? "; break; + } + memset(prot, ' ' , sizeof(prot)-1); + prot[sizeof(prot)-1] = '\0'; + if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE)) + prot[0] = 'R'; + if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) + prot[1] = 'W'; + if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY)) + prot[1] = 'C'; + if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE)) + prot[2] = 'X'; + } + else + { + type = ""; + prot[0] = '\0'; + } + packet_reply_open(gdbctx); + sprintf(buffer, "%08lx %08lx %s %s %s\n", + (DWORD)addr, mbi.RegionSize, state, type, prot); + packet_reply_catc(gdbctx, 'O'); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); + + if (addr + mbi.RegionSize < addr) /* wrap around ? */ + break; + addr += mbi.RegionSize; + } + packet_reply(gdbctx, "OK", 2); +} + +static void packet_query_monitor_trace(struct gdb_context* gdbctx, + int len, const char* str) +{ + char buffer[128]; + + if (len == 0) + { + sprintf(buffer, "trace=%x\n", gdbctx->trace); + } + else if (len >= 2 && str[0] == '=') + { + unsigned val = atoi(&str[1]); + sprintf(buffer, "trace: %x => %x\n", gdbctx->trace, val); + gdbctx->trace = val; + } + else + { + /* FIXME: ugly but can use error packet here */ + packet_reply_cat(gdbctx, "E00"); + return; + } + packet_reply_open(gdbctx); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); +} + +#ifdef __i386__ +static void packet_query_monitor_linear(struct gdb_context* gdbctx, + int len, const char* str) +{ + unsigned seg, ofs; + LDT_ENTRY le; + unsigned linear; + char buffer[32]; + + while (len > 0 && (*str == ' ' || *str == '\t')) + { + str++; len--; + } + /* FIXME: do a better scanning (allow both decimal and hex numbers) */ + if (!len || sscanf(str, "%x:%x", &seg, &ofs) != 2) + { + packet_reply_error(gdbctx, 0); + return; + } + + /* V86 mode ? */ + if (gdbctx->context.EFlags & 0x00020000) linear = (LOWORD(seg) << 4) + ofs; + /* linux system selector ? */ + else if (!(seg & 4) || ((seg >> 3) < 17)) linear = ofs; + /* standard selector */ + else if (GetThreadSelectorEntry(gdbctx->process->threads->handle, seg, &le)) + linear = (le.HighWord.Bits.BaseHi << 24) + (le.HighWord.Bits.BaseMid << 16) + + le.BaseLow + ofs; + /* error */ + else linear = 0; + sprintf(buffer, "0x%x", linear); + packet_reply_open(gdbctx); + packet_reply_hex_to(gdbctx, buffer, strlen(buffer)); + packet_reply_close(gdbctx); +} +#endif + +struct query_detail +{ + int with_arg; + const char* name; + size_t len; + void (*handler)(struct gdb_context*, int, const char*); +} query_details[] = +{ + {0, "wnd", 3, packet_query_monitor_wnd}, + {0, "window", 6, packet_query_monitor_wnd}, + {0, "proc", 4, packet_query_monitor_process}, + {0, "process", 7, packet_query_monitor_process}, + {0, "mem", 3, packet_query_monitor_mem}, + {1, "trace", 5, packet_query_monitor_trace}, +#ifdef __i386__ + {1, "linear", 6, packet_query_monitor_linear}, +#endif + {0, NULL, 0, NULL}, +}; + +static enum packet_return packet_query_remote_command(struct gdb_context* gdbctx, + const char* hxcmd, size_t len) +{ + char buffer[128]; + struct query_detail* qd; + + assert((len & 1) == 0 && len < 2 * sizeof(buffer)); + len /= 2; + hex_from(buffer, hxcmd, len); + + for (qd = &query_details[0]; qd->name != NULL; qd++) + { + if (len < qd->len || strncmp(buffer, qd->name, qd->len) != 0) continue; + if (!qd->with_arg && len != qd->len) continue; + + (qd->handler)(gdbctx, len - qd->len, buffer + qd->len); + return packet_done; + } + return packet_reply_error(gdbctx, EINVAL); +} + +static enum packet_return packet_query(struct gdb_context* gdbctx) +{ + switch (gdbctx->in_packet[0]) + { + case 'f': + if (strncmp(gdbctx->in_packet + 1, "ThreadInfo", gdbctx->in_packet_len - 1) == 0) + { + DBG_THREAD* thd; + + packet_reply_open(gdbctx); + packet_reply_add(gdbctx, "m", 1); + for (thd = gdbctx->process->threads; thd; thd = thd->next) + { + packet_reply_val(gdbctx, thd->tid, 4); + if (thd->next != NULL) + packet_reply_add(gdbctx, ",", 1); + } + packet_reply_close(gdbctx); + return packet_done; + } + else if (strncmp(gdbctx->in_packet + 1, "ProcessInfo", gdbctx->in_packet_len - 1) == 0) + { + char result[128]; + + packet_reply_open(gdbctx); + packet_reply_catc(gdbctx, 'O'); + get_process_info(gdbctx, result, sizeof(result)); + packet_reply_hex_to(gdbctx, result, strlen(result)); + packet_reply_close(gdbctx); + return packet_done; + } + break; + case 's': + if (strncmp(gdbctx->in_packet + 1, "ThreadInfo", gdbctx->in_packet_len - 1) == 0) + { + packet_reply(gdbctx, "l", 1); + return packet_done; + } + else if (strncmp(gdbctx->in_packet + 1, "ProcessInfo", gdbctx->in_packet_len - 1) == 0) + { + packet_reply(gdbctx, "l", 1); + return packet_done; + } + break; + case 'C': + if (gdbctx->in_packet_len == 1) + { + DBG_THREAD* thd; + /* FIXME: doc says 16 bit val ??? */ + /* grab first created thread, aka last in list */ + assert(gdbctx->process && gdbctx->process->threads); + for (thd = gdbctx->process->threads; thd->next; thd = thd->next); + packet_reply_open(gdbctx); + packet_reply_add(gdbctx, "QC", 2); + packet_reply_val(gdbctx, thd->tid, 4); + packet_reply_close(gdbctx); + return packet_done; + } + break; + case 'O': + if (strncmp(gdbctx->in_packet, "Offsets", gdbctx->in_packet_len) == 0) + { + char buf[64]; + + if (gdbctx->wine_segs[0] == 0 && gdbctx->wine_segs[1] == 0 && + gdbctx->wine_segs[2] == 0) + return packet_error; + sprintf(buf, "Text=%08lx;Data=%08lx;Bss=%08lx", + gdbctx->wine_segs[0], gdbctx->wine_segs[1], + gdbctx->wine_segs[2]); + return packet_reply(gdbctx, buf, -1); + } + break; + case 'R': + if (gdbctx->in_packet_len > 5 && strncmp(gdbctx->in_packet, "Rcmd,", 5) == 0) + { + return packet_query_remote_command(gdbctx, gdbctx->in_packet + 5, + gdbctx->in_packet_len - 5); + } + break; + case 'S': + if (strncmp(gdbctx->in_packet, "Symbol::", gdbctx->in_packet_len) == 0) + return packet_ok; + break; + case 'T': + if (gdbctx->in_packet_len > 15 && + strncmp(gdbctx->in_packet, "ThreadExtraInfo", 15) == 0 && + gdbctx->in_packet[15] == ',') + { + unsigned tid; + char* end; + char result[128]; + + tid = strtol(gdbctx->in_packet + 16, &end, 16); + if (end == NULL) break; + get_thread_info(gdbctx, tid, result, sizeof(result)); + packet_reply_open(gdbctx); + packet_reply_hex_to(gdbctx, result, strlen(result)); + packet_reply_close(gdbctx); + return packet_done; + } + break; + } + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "Unknown or malformed query %*.*s\n", + gdbctx->in_packet_len, gdbctx->in_packet_len, gdbctx->in_packet); + return packet_error; +} + +static enum packet_return packet_step(struct gdb_context* gdbctx) +{ + /* FIXME: add support for address in packet */ + assert(gdbctx->in_packet_len == 0); + if (DEBUG_CurrThread->tid != gdbctx->exec_thread && gdbctx->exec_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: step on %u, while last thd is %lu\n", + gdbctx->exec_thread, DEBUG_CurrThread->tid); + if (!cpu_enter_stepping(gdbctx)) return packet_error; + resume_debuggee(gdbctx, DBG_CONTINUE); + wait_for_debuggee(gdbctx); + if (!cpu_leave_stepping(gdbctx)) return packet_error; + return packet_reply_status(gdbctx); +} + +#if 0 +static enum packet_return packet_step_signal(struct gdb_context* gdbctx) +{ + unsigned char sig; + + /* FIXME: add support for address in packet */ + assert(gdbctx->in_packet_len == 2); + if (DEBUG_CurrThread->tid != gdbctx->exec_thread && gdbctx->exec_thread) + if (gdbctx->trace & GDBPXY_TRC_COMMAND_ERROR) + fprintf(stderr, "NIY: step/sig on %u, while last thd is %u\n", + gdbctx->exec_thread, DEBUG_CurrThread->tid); + hex_from(&sig, gdbctx->in_packet, 1); + /* cannot change signals on the fly */ + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "sigs: %u %u\n", sig, gdbctx->last_sig); + if (sig != gdbctx->last_sig) + return packet_error; + resume_debuggee(gdbctx, DBG_EXCEPTION_NOT_HANDLED); + wait_for_debuggee(gdbctx); + return packet_reply_status(gdbctx); +} +#endif + +static enum packet_return packet_thread_alive(struct gdb_context* gdbctx) +{ + char* end; + unsigned tid; + + tid = strtol(gdbctx->in_packet, &end, 16); + if (tid == -1 || tid == 0) + return packet_reply_error(gdbctx, EINVAL); + if (DEBUG_GetThread(gdbctx->process, tid) != NULL) + return packet_ok; + return packet_reply_error(gdbctx, ESRCH); +} + +static enum packet_return packet_remove_breakpoint(struct gdb_context* gdbctx) +{ + void* addr; + unsigned len; + struct gdb_ctx_Xpoint* xpt; + + /* FIXME: check packet_len */ + if (gdbctx->in_packet[0] < '0' || gdbctx->in_packet[0] > '4' || + gdbctx->in_packet[1] != ',' || + sscanf(gdbctx->in_packet + 2, "%p,%x", &addr, &len) != 2) + return packet_error; + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "remove bp %p[%u] typ=%c\n", + addr, len, gdbctx->in_packet[0]); + for (xpt = &gdbctx->Xpoints[NUM_XPOINT - 1]; xpt >= gdbctx->Xpoints; xpt--) + { + if (xpt->addr == addr && xpt->type == gdbctx->in_packet[0]) + { + switch (cpu_remove_Xpoint(gdbctx, xpt, len)) + { + case 1: xpt->type = -1; return packet_ok; + case 0: return packet_error; + case -1: return packet_done; + default: assert(0); + } + } + } + return packet_error; +} + +static enum packet_return packet_set_breakpoint(struct gdb_context* gdbctx) +{ + void* addr; + unsigned len; + struct gdb_ctx_Xpoint* xpt; + + /* FIXME: check packet_len */ + if (gdbctx->in_packet[0] < '0' || gdbctx->in_packet[0] > '4' || + gdbctx->in_packet[1] != ',' || + sscanf(gdbctx->in_packet + 2, "%p,%x", &addr, &len) != 2) + return packet_error; + if (gdbctx->trace & GDBPXY_TRC_COMMAND) + fprintf(stderr, "set bp %p[%u] typ=%c\n", + addr, len, gdbctx->in_packet[0]); + /* because of packet command handling, this should be made idempotent */ + for (xpt = &gdbctx->Xpoints[NUM_XPOINT - 1]; xpt >= gdbctx->Xpoints; xpt--) + { + if (xpt->addr == addr && xpt->type == gdbctx->in_packet[0]) + return packet_ok; /* nothing to do */ + } + /* really set the Xpoint */ + for (xpt = &gdbctx->Xpoints[NUM_XPOINT - 1]; xpt >= gdbctx->Xpoints; xpt--) + { + if (xpt->type == -1) + { + xpt->addr = addr; + xpt->type = gdbctx->in_packet[0]; + switch (cpu_insert_Xpoint(gdbctx, xpt, len)) + { + case 1: return packet_ok; + case 0: return packet_error; + case -1: return packet_done; + default: assert(0); + } + } + } + /* no more entries... eech */ + fprintf(stderr, "Running out of spot for {break|watcgh}points\n"); + return packet_error; +} + +/* =============================================== * + * P A C K E T I N F R A S T R U C T U R E * + * =============================================== * + */ + +struct packet_entry +{ + char key; + enum packet_return (*handler)(struct gdb_context* gdbctx); +}; + +static struct packet_entry packet_entries[] = +{ +/* {'!', packet_extended}, */ + {'?', packet_last_signal}, + {'c', packet_continue}, + {'C', packet_continue_signal}, + {'D', packet_detach}, + {'g', packet_read_registers}, + {'G', packet_write_registers}, + {'k', packet_kill}, + {'H', packet_thread}, + {'m', packet_read_memory}, + {'M', packet_write_memory}, + /* {'p', packet_read_register}, doesn't seem needed */ + {'P', packet_write_register}, + {'q', packet_query}, + {'s', packet_step}, + /*{'S', packet_step_signal}, hard(er) to implement */ + {'T', packet_thread_alive}, + {'z', packet_remove_breakpoint}, + {'Z', packet_set_breakpoint}, +}; + +static BOOL extract_packets(struct gdb_context* gdbctx) +{ + char* end; + int plen; + unsigned char in_cksum, loc_cksum; + char* ptr; + enum packet_return ret = packet_error; + + while ((ret & packet_last_f) == 0) + { + if (gdbctx->in_len && (gdbctx->trace & GDBPXY_TRC_LOWLEVEL)) + fprintf(stderr, "in-buf: %*.*s\n", + gdbctx->in_len, gdbctx->in_len, gdbctx->in_buf); + ptr = memchr(gdbctx->in_buf, '$', gdbctx->in_len); + if (ptr == NULL) return FALSE; + if (ptr != gdbctx->in_buf) + { + int glen = ptr - gdbctx->in_buf; /* garbage len */ + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "removing garbage: %*.*s\n", + glen, glen, gdbctx->in_buf); + gdbctx->in_len -= glen; + memmove(gdbctx->in_buf, ptr, gdbctx->in_len); + } + end = memchr(gdbctx->in_buf + 1, '#', gdbctx->in_len); + if (end == NULL) return FALSE; + /* no checksum yet */ + if (end + 3 > gdbctx->in_buf + gdbctx->in_len) return FALSE; + plen = end - gdbctx->in_buf - 1; + hex_from(&in_cksum, end + 1, 1); + loc_cksum = checksum(gdbctx->in_buf + 1, plen); + if (loc_cksum == in_cksum) + { + int i; + + ret = packet_error; + + write(gdbctx->sock, "+", 1); + assert(plen); + + /* FIXME: should use bsearch if packet_entries was sorted */ + for (i = 0; i < sizeof(packet_entries)/sizeof(packet_entries[0]); i++) + { + if (packet_entries[i].key == gdbctx->in_buf[1]) break; + } + if (i == sizeof(packet_entries)/sizeof(packet_entries[0])) + { + if (gdbctx->trace & GDBPXY_TRC_PACKET) + fprintf(stderr, "Unknown packet request %*.*s\n", + plen, plen, &gdbctx->in_buf[1]); + } + else + { + gdbctx->in_packet = gdbctx->in_buf + 2; + gdbctx->in_packet_len = plen - 1; + if (gdbctx->trace & GDBPXY_TRC_PACKET) + fprintf(stderr, "Packet: %c%*.*s\n", + gdbctx->in_buf[1], + gdbctx->in_packet_len, gdbctx->in_packet_len, + gdbctx->in_packet); + ret = (packet_entries[i].handler)(gdbctx); + } + switch (ret & ~packet_last_f) + { + case packet_error: packet_reply(gdbctx, "", 0); break; + case packet_ok: packet_reply(gdbctx, "OK", 2); break; + case packet_done: break; + } + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "reply-full: %*.*s\n", + gdbctx->out_len, gdbctx->out_len, gdbctx->out_buf); + i = write(gdbctx->sock, gdbctx->out_buf, gdbctx->out_len); + assert(i == gdbctx->out_len); + /* if this fails, we'll have to use POLLOUT... + */ + gdbctx->out_len = 0; + } + else if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + { + write(gdbctx->sock, "+", 1); + fprintf(stderr, "dropping packet, invalid checksum %d <> %d\n", in_cksum, loc_cksum); + } + gdbctx->in_len -= plen + 4; + memmove(gdbctx->in_buf, end + 3, gdbctx->in_len); + } + return TRUE; +} + +static int fetch_data(struct gdb_context* gdbctx) +{ + int len, in_len = gdbctx->in_len; + + assert(gdbctx->in_len <= gdbctx->in_buf_alloc); + for (;;) + { +#define STEP 128 + if (gdbctx->in_len + STEP > gdbctx->in_buf_alloc) + gdbctx->in_buf = realloc(gdbctx->in_buf, gdbctx->in_buf_alloc += STEP); +#undef STEP + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "%d %d %*.*s\n", + gdbctx->in_len, gdbctx->in_buf_alloc, + gdbctx->in_len, gdbctx->in_len, gdbctx->in_buf); + len = read(gdbctx->sock, gdbctx->in_buf + gdbctx->in_len, gdbctx->in_buf_alloc - gdbctx->in_len); + if (len <= 0) break; + gdbctx->in_len += len; + assert(gdbctx->in_len <= gdbctx->in_buf_alloc); + if (len < gdbctx->in_buf_alloc - gdbctx->in_len) break; + } + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "=> %d\n", gdbctx->in_len - in_len); + return gdbctx->in_len - in_len; +} + +static BOOL gdb_startup(struct gdb_context* gdbctx, DEBUG_EVENT* de, unsigned flags) +{ + int sock; + struct sockaddr_in s_addr; + socklen_t s_len = sizeof(s_addr); + struct pollfd pollfd; + char wine_path[MAX_PATH]; + char* ptr; + + /* step 1: create socket for gdb connection request */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "Can't create socket"); + return FALSE; + } + + if (listen(sock, 1) == -1 || + getsockname(sock, (struct sockaddr*)&s_addr, &s_len) == -1) + return FALSE; + + /* step 2: find out wine executable location (as a Unix filename) */ + ptr = getenv("WINELOADER"); + strcpy(wine_path, ptr ? ptr : "wine"); + + fprintf(stderr, "using wine_path: %s\n", wine_path); + read_elf_info(wine_path, gdbctx->wine_segs); + + /* step 3: fire up gdb (if requested) */ + if (flags & 1) + fprintf(stderr, "target remote localhost:%d\n", ntohs(s_addr.sin_port)); + else + switch (fork()) + { + case -1: /* error in parent... */ + fprintf(stderr, "Cannot create gdb\n"); + return FALSE; + break; + default: /* in parent... success */ + break; + case 0: /* in child... and alive */ + { + char buf[MAX_PATH]; + char* gdb_path; + FILE* f; + + if (!(gdb_path = getenv("WINE_GDB"))) gdb_path = "gdb"; + if (!tmpnam(buf) || (f = fopen(buf, "w+")) == NULL) return FALSE; + fprintf(f, "file %s\n", wine_path); + fprintf(f, "target remote localhost:%d\n", ntohs(s_addr.sin_port)); + fprintf(f, "monitor trace=0\n"); + fprintf(f, "set prompt Wine-gdb>\\ \n"); + /* tell gdb to delete this file when done handling it... */ + fprintf(f, "shell rm -f \"%s\"\n", buf); + fclose(f); + if (flags & 2) + execlp("xterm", "xterm", "-e", gdb_path, "-x", buf, NULL); + else + execlp(gdb_path, gdb_path, "-x", buf, NULL); + assert(0); /* never reached */ + break; + } + break; + } + + /* step 4: do the process internal creation */ + handle_debug_event(gdbctx, de); + + /* step 5: wait for gdb to connect actually */ + pollfd.fd = sock; + pollfd.events = POLLIN; + pollfd.revents = 0; + + switch (poll(&pollfd, 1, -1)) + { + case 1: + if (pollfd.revents & POLLIN) + { + int dummy = 1; + gdbctx->sock = accept(sock, (struct sockaddr*)&s_addr, &s_len); + if (gdbctx->sock == -1) + break; + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "Connected on %d\n", gdbctx->sock); + /* don't keep our small packets too long: send them ASAP back to GDB + * without this, GDB really crawls + */ + setsockopt(gdbctx->sock, IPPROTO_TCP, TCP_NODELAY, (char*)&dummy, sizeof(dummy)); + } + break; + case 0: + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "poll for cnx failed (timeout)\n"); + return FALSE; + case -1: + if (gdbctx->trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "poll for cnx failed (error)\n"); + return FALSE; + default: + assert(0); + } + + close(sock); + return TRUE; +} + +static BOOL gdb_init_context(struct gdb_context* gdbctx, unsigned flags) +{ + DEBUG_EVENT de; + int i; + + gdbctx->sock = -1; + gdbctx->in_buf = NULL; + gdbctx->in_buf_alloc = 0; + gdbctx->in_len = 0; + gdbctx->out_buf = NULL; + gdbctx->out_buf_alloc = 0; + gdbctx->out_len = 0; + gdbctx->out_curr_packet = -1; + + gdbctx->exec_thread = gdbctx->other_thread = 0; + gdbctx->last_sig = 0; + gdbctx->in_trap = FALSE; + gdbctx->trace = /*GDBPXY_TRC_PACKET | GDBPXY_TRC_COMMAND |*/ GDBPXY_TRC_COMMAND_ERROR | GDBPXY_TRC_WIN32_EVENT; + gdbctx->process = NULL; + for (i = 0; i < NUM_XPOINT; i++) + gdbctx->Xpoints[i].type = -1; + + /* wait for first trap */ + while (WaitForDebugEvent(&de, INFINITE)) + { + if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) + { + /* this should be the first event we get, + * and the only one of this type */ + assert(gdbctx->process == NULL && de.dwProcessId == DEBUG_CurrPid); + //gdbctx->dwProcessId = pid; + if (!gdb_startup(gdbctx, &de, flags)) return FALSE; + assert(!gdbctx->in_trap); + } + else + { + handle_debug_event(gdbctx, &de); + if (gdbctx->in_trap) break; + } + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); + } + return TRUE; +} + +BOOL DEBUG_GdbRemote(unsigned flags) +{ + struct pollfd pollfd; + struct gdb_context gdbctx; + BOOL doLoop; + + for (doLoop = gdb_init_context(&gdbctx, flags); doLoop;) + { + pollfd.fd = gdbctx.sock; + pollfd.events = POLLIN; + pollfd.revents = 0; + + switch (poll(&pollfd, 1, -1)) + { + case 1: + /* got something */ + if (pollfd.revents & (POLLHUP | POLLERR)) + { + if (gdbctx.trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "Gdb hung up\n"); + /* kill also debuggee process - questionnable - */ + detach_debuggee(&gdbctx, TRUE); + doLoop = FALSE; + break; + } + if ((pollfd.revents & POLLIN) && fetch_data(&gdbctx) > 0) + { + if (extract_packets(&gdbctx)) doLoop = FALSE; + } + break; + case 0: + /* timeout, should never happen (infinite timeout) */ + break; + case -1: + if (gdbctx.trace & GDBPXY_TRC_LOWLEVEL) + fprintf(stderr, "poll failed\n"); + doLoop = FALSE; + break; + } + } + wait(NULL); + return 0; +} +