diff options
Diffstat (limited to 'gdb/wince-stub.c')
-rw-r--r-- | gdb/wince-stub.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/gdb/wince-stub.c b/gdb/wince-stub.c new file mode 100644 index 0000000..09b8b8d --- /dev/null +++ b/gdb/wince-stub.c @@ -0,0 +1,536 @@ +/* wince-stub.c -- debugging stub for a Windows CE device + + Copyright 1999, 2000 Free Software Foundation, Inc. + Contributed by Cygnus Solutions, A Red Hat Company. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without eve nthe implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/* by Christopher Faylor (cgf@cygnus.com) */ + +#include <stdarg.h> +#include <windows.h> +#include <winsock.h> +#include "wince-stub.h" + +#define MALLOC(n) (void *) LocalAlloc (LMEM_MOVEABLE, (UINT)(n)) +#define REALLOC(s, n) (void *) LocalReAlloc ((HLOCAL)(s), (UINT)(n), LMEM_MOVEABLE) + +static int skip_next_id = 0; /* Don't read next API code from socket */ + +/* v-style interface for handling varying argyment list error messages. + Displays the error message in a dialog box and exits when user clicks + on OK. */ +static void +vstub_error (LPCWSTR fmt, va_list args) +{ + WCHAR buf[4096]; + wvsprintfW (buf, fmt, args); + + MessageBoxW (NULL, buf, L"GDB", MB_ICONERROR); + WSACleanup (); + ExitThread (1); +} + +/* The standard way to display an error message and exit. */ +static void +stub_error (LPCWSTR fmt, ...) +{ + va_list args; + va_start (args, fmt); + vstub_error (fmt, args); +} + +/* Standard "oh well" can't communicate error. Someday this might attempt + synchronization. */ +static void +attempt_resync (LPCWSTR huh, int s) +{ + stub_error (L"lost synchronization with host attempting %s. Error %d", huh, WSAGetLastError ()); +} + +/* Read arbitrary stuff from a socket. */ +static int +sockread (LPCWSTR huh, int s, void *str, size_t n) +{ + for (;;) + { + if (recv (s, str, n, 0) == (int) n) + return n; + attempt_resync (huh, s); + } +} + +/* Write arbitrary stuff to a socket. */ +static int +sockwrite (LPCWSTR huh, int s, const void *str, size_t n) +{ + for (;;) + { + if (send (s, str, n, 0) == (int) n) + return n; + attempt_resync (huh, s); + } +} + +/* Allocate a limited pool of memory, reallocating over unused + buffers. This assumes that there will never be more than four + "buffers" required which, so far, is a safe assumption. */ +static LPVOID +mempool (gdb_wince_len len) +{ + static int n = -1; + static LPWSTR outs[4] = {NULL /*, NULL, etc. */}; + + if (++n >= (sizeof (outs) / sizeof (outs[0]))) + n = 0; + + /* Allocate space for the converted string, reusing any previously allocated + space, if applicable. */ + if (outs[n]) + outs[n] = (LPWSTR) REALLOC (outs[n], len); + else + outs[n] = (LPWSTR) MALLOC (len); + + return outs[n]; +} + +/* Get a an ID (possibly) and a DWORD from the host gdb. + Don't bother with the id if the main loop has already + read it. */ +static DWORD +getdword (LPCWSTR huh, int s, gdb_wince_id what_this) +{ + DWORD n; + gdb_wince_id what; + + if (skip_next_id) + skip_next_id = 0; + else + do + if (sockread (huh, s, &what, sizeof (what)) != sizeof (what)) + stub_error (L"error getting record type from host - %s.", huh); + while (what_this != what); + + if (sockread (huh, s, &n, sizeof (n)) != sizeof (n)) + stub_error (L"error getting %s from host.", huh); + + return n; +} + +/* Get a an ID (possibly) and a WORD from the host gdb. + Don't bother with the id if the main loop has already + read it. */ +static WORD +getword (LPCWSTR huh, int s, gdb_wince_id what_this) +{ + WORD n; + gdb_wince_id what; + + if (skip_next_id) + skip_next_id = 0; + else + do + if (sockread (huh, s, &what, sizeof (what)) != sizeof (what)) + stub_error (L"error getting record type from host - %s.", huh); + while (what_this != what); + + if (sockread (huh, s, &n, sizeof (n)) != sizeof (n)) + stub_error (L"error getting %s from host.", huh); + + return n; +} + +/* Handy defines for getting various types of values. */ +#define gethandle(huh, s, what) (HANDLE) getdword ((huh), (s), (what)) +#define getpvoid(huh, s, what) (LPVOID) getdword ((huh), (s), (what)) +#define getlen(huh, s, what) (gdb_wince_len) getword ((huh), (s), (what)) + +/* Get an arbitrary block of memory from the gdb host. This comes in + two chunks an id/dword representing the length and the stream of memory + itself. Returns a pointer, allocated via mempool, to a memory buffer. */ +static LPWSTR +getmemory (LPCWSTR huh, int s, gdb_wince_id what, gdb_wince_len *inlen) +{ + LPVOID p; + gdb_wince_len dummy; + + if (!inlen) + inlen = &dummy; + + *inlen = getlen (huh, s, what); + + p = mempool (*inlen); /* FIXME: check for error */ + + if ((gdb_wince_len) sockread (huh, s, p, *inlen) != *inlen) + stub_error (L"error getting string from host."); + + return p; +} + +/* Output an id/dword to the host */ +static void +putdword (LPCWSTR huh, int s, gdb_wince_id what, DWORD n) +{ + if (sockwrite (huh, s, &what, sizeof (what)) != sizeof (what)) + stub_error (L"error writing record id for %s to host.", huh); + if (sockwrite (huh, s, &n, sizeof (n)) != sizeof (n)) + stub_error (L"error writing %s to host.", huh); +} + +/* Output an id/word to the host */ +static void +putword (LPCWSTR huh, int s, gdb_wince_id what, WORD n) +{ + if (sockwrite (huh, s, &what, sizeof (what)) != sizeof (what)) + stub_error (L"error writing record id for %s to host.", huh); + if (sockwrite (huh, s, &n, sizeof (n)) != sizeof (n)) + stub_error (L"error writing %s to host.", huh); +} + +/* Convenience define for outputting a "gdb_wince_len" type. */ +#define putlen(huh, s, what, n) putword ((huh), (s), (what), (gdb_wince_len) (n)) + +/* Put an arbitrary block of memory to the gdb host. This comes in + two chunks an id/dword representing the length and the stream of memory + itself. */ +static void +putmemory (LPCWSTR huh, int s, gdb_wince_id what, const void *mem, gdb_wince_len len) +{ + putlen (huh, s, what, len); + if (((short) len > 0) && (gdb_wince_len) sockwrite (huh, s, mem, len) != len) + stub_error (L"error writing memory to host."); +} + +/* Output the result of an operation to the host. If res != 0, sends a block of + memory starting at mem of len bytes. If res == 0, sends -GetLastError () and + avoids sending the mem. */ +static void +putresult (LPCWSTR huh, gdb_wince_result res, int s, gdb_wince_id what, const void *mem, gdb_wince_len len) +{ + if (!res) + len = -(int) GetLastError (); + putmemory (huh, s, what, mem, len); +} + +static HANDLE curproc; /* Currently unused, but nice for debugging */ + +/* Emulate CreateProcess. Returns &pi if no error. */ +static void +create_process (int s) +{ + LPWSTR exec_file = getmemory (L"CreateProcess exec_file", s, GDB_CREATEPROCESS, NULL); + LPWSTR args = getmemory (L"CreateProcess args", s, GDB_CREATEPROCESS, NULL); + DWORD flags = getdword (L"CreateProcess flags", s, GDB_CREATEPROCESS); + PROCESS_INFORMATION pi; + gdb_wince_result res; + + res = CreateProcessW (exec_file, + args, /* command line */ + NULL, /* Security */ + NULL, /* thread */ + FALSE, /* inherit handles */ + flags, /* start flags */ + NULL, + NULL, /* current directory */ + NULL, + &pi); + putresult (L"CreateProcess", res, s, GDB_CREATEPROCESS, &pi, sizeof (pi)); + curproc = pi.hProcess; +} + +/* Emulate TerminateProcess. Returns return value of TerminateProcess if + no error. + *** NOTE: For some unknown reason, TerminateProcess seems to always return + an ACCESS_DENIED (on Windows CE???) error. So, force a TRUE value for now. */ +static void +terminate_process (int s) +{ + gdb_wince_result res; + HANDLE h = gethandle (L"TerminateProcess handle", s, GDB_TERMINATEPROCESS); + + res = TerminateProcess (h, 0) || 1; /* Doesn't seem to work on SH so default to TRUE */ + putresult (L"Terminate process result", res, s, GDB_TERMINATEPROCESS, + &res, sizeof (res)); +} + +/* Emulate WaitForDebugEvent. Returns the debug event on success. */ +static void +wait_for_debug_event (int s) +{ + DWORD ms = getdword (L"WaitForDebugEvent ms", s, GDB_WAITFORDEBUGEVENT); + gdb_wince_result res; + DEBUG_EVENT ev; + + res = WaitForDebugEvent (&ev, ms); + putresult (L"WaitForDebugEvent event", res, s, GDB_WAITFORDEBUGEVENT, + &ev, sizeof (ev)); +} + +/* Emulate GetThreadContext. Returns CONTEXT structure on success. */ +static void +get_thread_context (int s) +{ + CONTEXT c; + HANDLE h = gethandle (L"GetThreadContext handle", s, GDB_GETTHREADCONTEXT); + gdb_wince_result res; + + memset (&c, 0, sizeof (c)); + c.ContextFlags = getdword (L"GetThreadContext handle", s, GDB_GETTHREADCONTEXT); + + res = (gdb_wince_result) GetThreadContext (h, &c); + putresult (L"GetThreadContext data", res, s, GDB_GETTHREADCONTEXT, + &c, sizeof (c)); +} + +/* Emulate GetThreadContext. Returns success of SetThreadContext. */ +static void +set_thread_context (int s) +{ + gdb_wince_result res; + HANDLE h = gethandle (L"SetThreadContext handle", s, GDB_SETTHREADCONTEXT); + LPCONTEXT pc = (LPCONTEXT) getmemory (L"SetThreadContext context", s, + GDB_SETTHREADCONTEXT, NULL); + + res = SetThreadContext (h, pc); + putresult (L"SetThreadContext result", res, s, GDB_SETTHREADCONTEXT, + &res, sizeof (res)); +} + +/* Emulate ReadProcessMemory. Returns memory read on success. */ +static void +read_process_memory (int s) +{ + HANDLE h = gethandle (L"ReadProcessMemory handle", s, GDB_READPROCESSMEMORY); + LPVOID p = getpvoid (L"ReadProcessMemory base", s, GDB_READPROCESSMEMORY); + gdb_wince_len len = getlen (L"ReadProcessMemory size", s, GDB_READPROCESSMEMORY); + LPVOID buf = mempool ((gdb_wince_len) len); + DWORD outlen; + gdb_wince_result res; + + outlen = 0; + res = (gdb_wince_result) ReadProcessMemory (h, p, buf, len, &outlen); + putresult (L"ReadProcessMemory data", res, s, GDB_READPROCESSMEMORY, + buf, (gdb_wince_len) outlen); +} + +/* Emulate WriteProcessMemory. Returns WriteProcessMemory success. */ +static void +write_process_memory (int s) +{ + HANDLE h = gethandle (L"WriteProcessMemory handle", s, GDB_WRITEPROCESSMEMORY); + LPVOID p = getpvoid (L"WriteProcessMemory base", s, GDB_WRITEPROCESSMEMORY); + gdb_wince_len len; + LPVOID buf = getmemory (L"WriteProcessMemory buf", s, GDB_WRITEPROCESSMEMORY, &len); + DWORD outlen; + gdb_wince_result res; + + outlen = 0; + res = WriteProcessMemory (h, p, buf, (DWORD) len, &outlen); + putresult (L"WriteProcessMemory data", res, s, GDB_WRITEPROCESSMEMORY, + (gdb_wince_len *) & outlen, sizeof (gdb_wince_len)); +} + +/* Return non-zero to gdb host if given thread is alive. */ +static void +thread_alive (int s) +{ + HANDLE h = gethandle (L"ThreadAlive handle", s, GDB_THREADALIVE); + gdb_wince_result res; + + res = WaitForSingleObject (h, 0) == WAIT_OBJECT_0 ? 1 : 0; + putresult (L"WriteProcessMemory data", res, s, GDB_THREADALIVE, + &res, sizeof (res)); +} + +/* Emulate SuspendThread. Returns value returned from SuspendThread. */ +static void +suspend_thread (int s) +{ + DWORD res; + HANDLE h = gethandle (L"SuspendThread handle", s, GDB_SUSPENDTHREAD); + res = SuspendThread (h); + putdword (L"SuspendThread result", s, GDB_SUSPENDTHREAD, res); +} + +/* Emulate ResumeThread. Returns value returned from ResumeThread. */ +static void +resume_thread (int s) +{ + DWORD res; + HANDLE h = gethandle (L"ResumeThread handle", s, GDB_RESUMETHREAD); + res = ResumeThread (h); + putdword (L"ResumeThread result", s, GDB_RESUMETHREAD, res); +} + +/* Emulate ContinueDebugEvent. Returns ContinueDebugEvent success. */ +static void +continue_debug_event (int s) +{ + gdb_wince_result res; + DWORD pid = getdword (L"ContinueDebugEvent pid", s, GDB_CONTINUEDEBUGEVENT); + DWORD tid = getdword (L"ContinueDebugEvent tid", s, GDB_CONTINUEDEBUGEVENT); + DWORD status = getdword (L"ContinueDebugEvent status", s, GDB_CONTINUEDEBUGEVENT); + res = (gdb_wince_result) ContinueDebugEvent (pid, tid, status); + putresult (L"ContinueDebugEvent result", res, s, GDB_CONTINUEDEBUGEVENT, &res, sizeof (res)); +} + +/* Emulate CloseHandle. Returns CloseHandle success. */ +static void +close_handle (int s) +{ + gdb_wince_result res; + HANDLE h = gethandle (L"CloseHandle handle", s, GDB_CLOSEHANDLE); + res = (gdb_wince_result) CloseHandle (h); + putresult (L"CloseHandle result", res, s, GDB_CLOSEHANDLE, &res, sizeof (res)); +} + +/* Handle single step instruction */ +static void +single_step (int s) +{ +} + +/* Main loop for reading requests from gdb host on the socket. */ +static void +dispatch (int s) +{ + gdb_wince_id id; + + /* Continue reading from socket until receive a GDB_STOPSUB. */ + while (sockread (L"Dispatch", s, &id, sizeof (id)) > 0) + { + skip_next_id = 1; + switch (id) + { + case GDB_CREATEPROCESS: + create_process (s); + break; + case GDB_TERMINATEPROCESS: + terminate_process (s); + break; + case GDB_WAITFORDEBUGEVENT: + wait_for_debug_event (s); + break; + case GDB_GETTHREADCONTEXT: + get_thread_context (s); + break; + case GDB_SETTHREADCONTEXT: + set_thread_context (s); + break; + case GDB_READPROCESSMEMORY: + read_process_memory (s); + break; + case GDB_WRITEPROCESSMEMORY: + write_process_memory (s); + break; + case GDB_THREADALIVE: + thread_alive (s); + break; + case GDB_SUSPENDTHREAD: + suspend_thread (s); + break; + case GDB_RESUMETHREAD: + resume_thread (s); + break; + case GDB_CONTINUEDEBUGEVENT: + continue_debug_event (s); + break; + case GDB_CLOSEHANDLE: + close_handle (s); + break; + case GDB_STOPSTUB: + terminate_process (s); + return; + case GDB_SINGLESTEP: + single_step (s); + return; + default: + { + WCHAR buf[80]; + wsprintfW (buf, L"Invalid command id received: %d", id); + MessageBoxW (NULL, buf, L"GDB", MB_ICONERROR); + skip_next_id = 0; + } + } + } +} + +/* The Windows Main entry point */ +int WINAPI +WinMain (HINSTANCE hi, HINSTANCE hp, LPWSTR cmd, int show) +{ + struct hostent *h; + int s; + struct WSAData wd; + struct sockaddr_in sin; + int tmp; + LPWSTR whost; + char host[80]; + + whost = wcschr (cmd, L' '); /* Look for argument. */ + whost = cmd; + + /* If no host is specified, just use default */ + if (whost) + { + /* Eat any spaces. */ + while (*whost == L' ' || *whost == L'\t') + whost++; + + wcstombs (host, whost, 80); /* Convert from UNICODE to ascii */ + } + + MessageBoxW (NULL, whost, L"GDB", MB_ICONERROR); + + /* Winsock initialization. */ + if (WSAStartup (MAKEWORD (1, 1), &wd)) + stub_error (L"Couldn't initialize WINSOCK."); + + /* If whost was specified, first try it. If it was not specified or the + host lookup failed, try the Windows CE magic ppp_peer lookup. ppp_peer + is supposed to be the Windows host sitting on the other end of the + serial cable. */ + if (whost && *whost && (h = gethostbyname (host)) != NULL) + /* nothing to do */ ; + else if ((h = gethostbyname ("ppp_peer")) == NULL) + stub_error (L"Couldn't get IP address of host system. Error %d", WSAGetLastError ()); + + /* Get a socket. */ + if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) + stub_error (L"Couldn't connect to host system. Error %d", WSAGetLastError ()); + + /* Allow rapid reuse of the port. */ + tmp = 1; + setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)); + + /* Set up the information for connecting to the host gdb process. */ + memset (&sin, 0, sizeof (sin)); + sin.sin_family = h->h_addrtype; + memcpy (&sin.sin_addr, h->h_addr, h->h_length); + sin.sin_port = htons (7000); /* FIXME: This should be configurable */ + + /* Connect to host */ + if (connect (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) + stub_error (L"Couldn't connect to host gdb."); + + /* Read from socket until told to exit. */ + dispatch (s); + WSACleanup (); + return 0; +} |