summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <me+cygwin@cgf.cx>2000-02-24 21:39:15 +0000
committerChristopher Faylor <me+cygwin@cgf.cx>2000-02-24 21:39:15 +0000
commit032ba4156a98615e06f57c1cd2aa0afc61dd8ed6 (patch)
treed5afa787cb4d1b80aa2b094f5d9684dd09b428d7
parent763da9480daceba3cfad4608aa8fc80cad525c41 (diff)
downloadgdb-032ba4156a98615e06f57c1cd2aa0afc61dd8ed6.tar.gz
Windows CE support.
-rw-r--r--gdb/wince-stub.c536
-rw-r--r--gdb/wince-stub.h48
-rw-r--r--gdb/wince.c2034
3 files changed, 2618 insertions, 0 deletions
diff --git a/gdb/wince-stub.c b/gdb/wince-stub.c
new file mode 100644
index 00000000000..09b8b8d111b
--- /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;
+}
diff --git a/gdb/wince-stub.h b/gdb/wince-stub.h
new file mode 100644
index 00000000000..21e9002116f
--- /dev/null
+++ b/gdb/wince-stub.h
@@ -0,0 +1,48 @@
+/* wince-stub.h -- Definitions for commnicating with the WinCE stub.
+
+ 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) */
+
+enum win_func
+ {
+ GDB_CREATEPROCESS = 42,
+ GDB_TERMINATEPROCESS,
+ GDB_WAITFORDEBUGEVENT,
+ GDB_GETTHREADCONTEXT,
+ GDB_SETTHREADCONTEXT,
+ GDB_READPROCESSMEMORY,
+ GDB_WRITEPROCESSMEMORY,
+ GDB_THREADALIVE,
+ GDB_SUSPENDTHREAD,
+ GDB_RESUMETHREAD,
+ GDB_CONTINUEDEBUGEVENT,
+ GDB_CLOSEHANDLE,
+ GDB_STOPSTUB,
+ GDB_SINGLESTEP,
+ GDB_SETBREAK,
+ GDB_INVALID
+ };
+
+typedef unsigned char gdb_wince_id;
+typedef unsigned short gdb_wince_len;
+typedef short gdb_wince_result;
diff --git a/gdb/wince.c b/gdb/wince.c
new file mode 100644
index 00000000000..bdd7c2f08e3
--- /dev/null
+++ b/gdb/wince.c
@@ -0,0 +1,2034 @@
+/* Target-vector operations for controlling Windows CE child processes, for GDB.
+ 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) */
+
+/* We assume we're being built with and will be used for cygwin. */
+
+#ifdef SHx
+#undef SH4
+#define SH4 /* Just to get all of the CONTEXT defines. */
+#endif
+
+#include "defs.h"
+#include "frame.h" /* required by inferior.h */
+#include "inferior.h"
+#include "target.h"
+#include "wait.h"
+#include "gdbcore.h"
+#include "command.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <windows.h>
+#include <rapi.h>
+#include <netdb.h>
+#include <cygwin/in.h>
+#include <cygwin/socket.h>
+
+#include "buildsym.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdb_string.h"
+#include "gdbthread.h"
+#include "gdbcmd.h"
+#include <sys/param.h>
+#include "wince-stub.h"
+#include "dcache.h"
+
+/* The ui's event loop. */
+extern int (*ui_loop_hook) PARAMS ((int signo));
+
+/* If we're not using the old Cygwin header file set, define the
+ following which never should have been in the generic Win32 API
+ headers in the first place since they were our own invention... */
+#ifndef _GNU_H_WINDOWS_H
+#define FLAG_TRACE_BIT 0x100
+#ifdef CONTEXT_FLOATING_POINT
+#define CONTEXT_DEBUGGER0 (CONTEXT_FULL | CONTEXT_FLOATING_POINT)
+#else
+#define CONTEXT_DEBUGGER0 (CONTEXT_FULL)
+#endif
+#endif
+
+#ifdef SH4
+#define CONTEXT_DEBUGGER ((CONTEXT_DEBUGGER0 & ~(CONTEXT_SH4 | CONTEXT_FLOATING_POINT)) | CONTEXT_SH3)
+#else
+#define CONTEXT_DEBUGGER CONTEXT_DEBUGGER0
+#endif
+/* The string sent by cygwin when it processes a signal.
+ FIXME: This should be in a cygwin include file. */
+#define CYGWIN_SIGNAL_STRING "cygwin: signal"
+
+#define CHECK(x) check (x, __FILE__,__LINE__)
+#define DEBUG_EXEC(x) if (debug_exec) printf x
+#define DEBUG_EVENTS(x) if (debug_events) printf x
+#define DEBUG_MEM(x) if (debug_memory) printf x
+#define DEBUG_EXCEPT(x) if (debug_exceptions) printf x
+
+static int connection_initialized = 0; /* True if we've initialized a RAPI session. */
+
+static DCACHE *remote_dcache;
+
+/* The directory where the stub and executable files are uploaded. */
+static const char *remote_directory = "\\gdb";
+
+/* The types automatic upload available. */
+static enum
+ {
+ UPLOAD_ALWAYS = 0,
+ UPLOAD_NEWER = 1,
+ UPLOAD_NEVER = 2
+ }
+upload_when = UPLOAD_NEWER;
+
+/* Valid options for 'set remoteupload'. Note that options
+ must track upload_when enum. */
+static struct opts
+ {
+ const char *name;
+ int abbrev;
+ }
+upload_options[3] =
+{
+ {
+ "always", 1
+ }
+ ,
+ {
+ "newer", 3
+ }
+ ,
+ {
+ "never", 3
+ }
+};
+
+static char *remote_upload = NULL; /* Set by set remoteupload */
+static int remote_add_host = 0;
+
+/* Forward declaration */
+extern struct target_ops child_ops;
+
+static int win32_child_thread_alive PARAMS ((int));
+void child_kill_inferior PARAMS ((void));
+
+static int last_sig = 0; /* Set if a signal was received from the
+ debugged process */
+
+/* Thread information structure used to track information that is
+ not available in gdb's thread structure. */
+typedef struct thread_info_struct
+ {
+ struct thread_info_struct *next;
+ DWORD id;
+ HANDLE h;
+ char *name;
+ int suspend_count;
+ int stepped; /* True if stepped. */
+ CORE_ADDR step_pc;
+ unsigned long step_instr;
+ unsigned long step_prev;
+ CONTEXT context;
+ }
+thread_info;
+
+static thread_info thread_head =
+{NULL};
+
+/* The process and thread handles for the above context. */
+
+static DEBUG_EVENT current_event; /* The current debug event from
+ WaitForDebugEvent */
+static HANDLE current_process_handle; /* Currently executing process */
+static thread_info *current_thread; /* Info on currently selected thread */
+static thread_info *this_thread; /* Info on thread returned by wait_for_debug_event */
+static DWORD main_thread_id; /* Thread ID of the main thread */
+
+/* Counts of things. */
+static int exception_count = 0;
+static int event_count = 0;
+
+/* User options. */
+static int debug_exec = 0; /* show execution */
+static int debug_events = 0; /* show events from kernel */
+static int debug_memory = 0; /* show target memory accesses */
+static int debug_exceptions = 0; /* show target exceptions */
+
+/* An array of offset mappings into a Win32 Context structure.
+ This is a one-to-one mapping which is indexed by gdb's register
+ numbers. It retrieves an offset into the context structure where
+ the 4 byte register is located.
+ An offset value of -1 indicates that Win32 does not provide this
+ register in it's CONTEXT structure. regptr will return zero for this
+ register.
+
+ This is used by the regptr function. */
+#define context_offset(x) ((int)&(((PCONTEXT)NULL)->x))
+static const int mappings[NUM_REGS + 1] =
+{
+#ifdef __i386__
+ context_offset (Eax),
+ context_offset (Ecx),
+ context_offset (Edx),
+ context_offset (Ebx),
+ context_offset (Esp),
+ context_offset (Ebp),
+ context_offset (Esi),
+ context_offset (Edi),
+ context_offset (Eip),
+ context_offset (EFlags),
+ context_offset (SegCs),
+ context_offset (SegSs),
+ context_offset (SegDs),
+ context_offset (SegEs),
+ context_offset (SegFs),
+ context_offset (SegGs),
+ context_offset (FloatSave.RegisterArea[0 * 10]),
+ context_offset (FloatSave.RegisterArea[1 * 10]),
+ context_offset (FloatSave.RegisterArea[2 * 10]),
+ context_offset (FloatSave.RegisterArea[3 * 10]),
+ context_offset (FloatSave.RegisterArea[4 * 10]),
+ context_offset (FloatSave.RegisterArea[5 * 10]),
+ context_offset (FloatSave.RegisterArea[6 * 10]),
+ context_offset (FloatSave.RegisterArea[7 * 10]),
+#elif defined(SHx)
+ context_offset (R0),
+ context_offset (R1),
+ context_offset (R2),
+ context_offset (R3),
+ context_offset (R4),
+ context_offset (R5),
+ context_offset (R6),
+ context_offset (R7),
+ context_offset (R8),
+ context_offset (R9),
+ context_offset (R10),
+ context_offset (R11),
+ context_offset (R12),
+ context_offset (R13),
+ context_offset (R14),
+ context_offset (R15),
+ context_offset (Fir),
+ context_offset (PR), /* Procedure Register */
+ context_offset (GBR), /* Global Base Register */
+ context_offset (MACH), /* Accumulate */
+ context_offset (MACL), /* Multiply */
+ context_offset (Psr),
+ context_offset (Fpul),
+ context_offset (Fpscr),
+ context_offset (FRegs[0]),
+ context_offset (FRegs[1]),
+ context_offset (FRegs[2]),
+ context_offset (FRegs[3]),
+ context_offset (FRegs[4]),
+ context_offset (FRegs[5]),
+ context_offset (FRegs[6]),
+ context_offset (FRegs[7]),
+ context_offset (FRegs[8]),
+ context_offset (FRegs[9]),
+ context_offset (FRegs[10]),
+ context_offset (FRegs[11]),
+ context_offset (FRegs[12]),
+ context_offset (FRegs[13]),
+ context_offset (FRegs[14]),
+ context_offset (FRegs[15]),
+ context_offset (xFRegs[0]),
+ context_offset (xFRegs[1]),
+ context_offset (xFRegs[2]),
+ context_offset (xFRegs[3]),
+ context_offset (xFRegs[4]),
+ context_offset (xFRegs[5]),
+ context_offset (xFRegs[6]),
+ context_offset (xFRegs[7]),
+ context_offset (xFRegs[8]),
+ context_offset (xFRegs[9]),
+ context_offset (xFRegs[10]),
+ context_offset (xFRegs[11]),
+ context_offset (xFRegs[12]),
+ context_offset (xFRegs[13]),
+ context_offset (xFRegs[14]),
+ context_offset (xFRegs[15]),
+#elif defined(MIPS)
+ context_offset (IntZero),
+ context_offset (IntAt),
+ context_offset (IntV0),
+ context_offset (IntV1),
+ context_offset (IntA0),
+ context_offset (IntA1),
+ context_offset (IntA2),
+ context_offset (IntA3),
+ context_offset (IntT0),
+ context_offset (IntT1),
+ context_offset (IntT2),
+ context_offset (IntT3),
+ context_offset (IntT4),
+ context_offset (IntT5),
+ context_offset (IntT6),
+ context_offset (IntT7),
+ context_offset (IntS0),
+ context_offset (IntS1),
+ context_offset (IntS2),
+ context_offset (IntS3),
+ context_offset (IntS4),
+ context_offset (IntS5),
+ context_offset (IntS6),
+ context_offset (IntS7),
+ context_offset (IntT8),
+ context_offset (IntT9),
+ context_offset (IntK0),
+ context_offset (IntK1),
+ context_offset (IntGp),
+ context_offset (IntSp),
+ context_offset (IntS8),
+ context_offset (IntRa),
+ context_offset (Psr),
+ context_offset (IntLo),
+ context_offset (IntHi),
+ -1, /* bad */
+ -1, /* cause */
+ context_offset (Fir),
+ context_offset (FltF0),
+ context_offset (FltF1),
+ context_offset (FltF2),
+ context_offset (FltF3),
+ context_offset (FltF4),
+ context_offset (FltF5),
+ context_offset (FltF6),
+ context_offset (FltF7),
+ context_offset (FltF8),
+ context_offset (FltF9),
+ context_offset (FltF10),
+ context_offset (FltF11),
+ context_offset (FltF12),
+ context_offset (FltF13),
+ context_offset (FltF14),
+ context_offset (FltF15),
+ context_offset (FltF16),
+ context_offset (FltF17),
+ context_offset (FltF18),
+ context_offset (FltF19),
+ context_offset (FltF20),
+ context_offset (FltF21),
+ context_offset (FltF22),
+ context_offset (FltF23),
+ context_offset (FltF24),
+ context_offset (FltF25),
+ context_offset (FltF26),
+ context_offset (FltF27),
+ context_offset (FltF28),
+ context_offset (FltF29),
+ context_offset (FltF30),
+ context_offset (FltF31),
+ context_offset (Fsr),
+ context_offset (Fir),
+ -1, /* fp */
+#elif defined(ARM)
+ context_offset (R0),
+ context_offset (R1),
+ context_offset (R2),
+ context_offset (R3),
+ context_offset (R4),
+ context_offset (R5),
+ context_offset (R6),
+ context_offset (R7),
+ context_offset (R8),
+ context_offset (R9),
+ context_offset (R10),
+ context_offset (R11),
+ context_offset (R12),
+ context_offset (Sp),
+ context_offset (Lr),
+ context_offset (Pc),
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ context_offset (Psr),
+#endif
+ -1
+};
+
+/* This vector maps the target's idea of an exception (extracted
+ from the DEBUG_EVENT structure) to GDB's idea. */
+
+struct xlate_exception
+ {
+ int them;
+ enum target_signal us;
+ };
+
+static const struct xlate_exception
+ xlate[] =
+{
+ {EXCEPTION_ACCESS_VIOLATION, TARGET_SIGNAL_SEGV},
+ {STATUS_STACK_OVERFLOW, TARGET_SIGNAL_SEGV},
+ {EXCEPTION_BREAKPOINT, TARGET_SIGNAL_TRAP},
+ {DBG_CONTROL_C, TARGET_SIGNAL_INT},
+ {EXCEPTION_SINGLE_STEP, TARGET_SIGNAL_TRAP},
+ {-1, -1}};
+
+/******************** Beginning of stub interface ********************/
+
+/* Stub interface description:
+
+ The Windows CE stub implements a crude RPC. The hand-held device
+ connects to gdb using port 7000. gdb and the stub then communicate
+ using packets where:
+
+ byte 0: command id (e.g. Create Process)
+
+ byte 1-4: DWORD
+
+ byte 1-2: WORD
+
+ byte 1-2: length
+ byte 3-n: arbitrary memory.
+
+ The interface is deterministic, i.e., if the stub expects a DWORD then
+ the gdb server should send a DWORD.
+ */
+
+/* Note: In the functions below, the `huh' parameter is a string passed from the
+ function containing a descriptive string concerning the current operation.
+ This is used for error reporting.
+
+ The 'what' parameter is a command id as found in wince-stub.h.
+
+ Hopefully, the rest of the parameters are self-explanatory.
+ */
+
+static int s; /* communication 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 (LPCSTR fmt, va_list * args)
+{
+ char buf[4096];
+ vsprintf (buf, fmt, args);
+ s = -1;
+ error ("%s", buf);
+}
+
+/* The standard way to display an error message and exit. */
+static void
+stub_error (LPCSTR 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 (LPCSTR huh, int s)
+{
+ stub_error ("lost synchronization with target attempting %s", huh);
+}
+
+/* Read arbitrary stuff from a socket. */
+static int
+sockread (LPCSTR huh, int s, void *str, size_t n)
+{
+ for (;;)
+ {
+ if (recv (s, str, n, 0) == n)
+ return n;
+ attempt_resync (huh, s);
+ }
+}
+
+/* Write arbitrary stuff to a socket. */
+static int
+sockwrite (LPCSTR huh, const void *str, size_t n)
+{
+ for (;;)
+ {
+ if (send (s, str, n, 0) == n)
+ return n;
+ attempt_resync (huh, s);
+ }
+}
+
+/* Output an id/dword to the host */
+static void
+putdword (LPCSTR huh, gdb_wince_id what, DWORD n)
+{
+ if (sockwrite (huh, &what, sizeof (what)) != sizeof (what))
+ stub_error ("error writing record id to host for %s", huh);
+ if (sockwrite (huh, &n, sizeof (n)) != sizeof (n))
+ stub_error ("error writing %s to host.", huh);
+}
+
+/* Output an id/word to the host */
+static void
+putword (LPCSTR huh, gdb_wince_id what, WORD n)
+{
+ if (sockwrite (huh, &what, sizeof (what)) != sizeof (what))
+ stub_error ("error writing record id to host for %s", huh);
+ if (sockwrite (huh, &n, sizeof (n)) != sizeof (n))
+ stub_error ("error writing %s host.", huh);
+}
+
+/* Convenience define for outputting a "gdb_wince_len" type. */
+#define putlen(huh, what, n) putword((huh), (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 (LPCSTR huh, gdb_wince_id what, const void *mem, gdb_wince_len len)
+{
+ putlen (huh, what, len);
+ if (((short) len > 0) && sockwrite (huh, mem, len) != len)
+ stub_error ("error writing %s to host.", huh);
+}
+
+/* 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 DWORD
+getdword (LPCSTR huh, gdb_wince_id what_this)
+{
+ DWORD n;
+ gdb_wince_id what;
+ do
+ if (sockread (huh, s, &what, sizeof (what)) != sizeof (what))
+ stub_error ("error getting record type from host - %s.", huh);
+ while (what_this != what);
+
+ if (sockread (huh, s, &n, sizeof (n)) != sizeof (n))
+ stub_error ("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 (LPCSTR huh, gdb_wince_id what_this)
+{
+ WORD n;
+ gdb_wince_id what;
+ do
+ if (sockread (huh, s, &what, sizeof (what)) != sizeof (what))
+ stub_error ("error getting record type from host - %s.", huh);
+ while (what_this != what);
+
+ if (sockread (huh, s, &n, sizeof (n)) != sizeof (n))
+ stub_error ("error getting %s from host.", huh);
+
+ return n;
+}
+
+/* Handy defines for getting/putting various types of values. */
+#define gethandle(huh, what) (HANDLE) getdword ((huh), (what))
+#define getpvoid(huh, what) (LPVOID) getdword ((huh), (what))
+#define getlen(huh, what) (gdb_wince_len) getword ((huh), (what))
+#define puthandle(huh, what, h) putdword ((huh), (what), (DWORD) (h))
+#define putpvoid(huh, what, p) putdword ((huh), (what), (DWORD) (p))
+
+/* Retrieve the result of an operation from the stub. If nbytes < 0) then nbytes
+ is actually an error and nothing else follows. Use SetLastError to remember this.
+ if nbytes > 0, retrieve a block of *nbytes into buf.
+ */
+int
+getresult (LPCSTR huh, gdb_wince_id what, LPVOID buf, gdb_wince_len * nbytes)
+{
+ gdb_wince_len dummy;
+ if (nbytes == NULL)
+ nbytes = &dummy;
+
+ *nbytes = getlen (huh, what);
+
+ if ((short) *nbytes < 0)
+ {
+ SetLastError (-(short) *nbytes);
+ return 0;
+ }
+
+ if ((gdb_wince_len) sockread (huh, s, buf, *nbytes) != *nbytes)
+ stub_error ("couldn't read information from wince stub - %s", huh);
+
+ return 1;
+}
+
+/* Convert "narrow" string to "wide". Manipulates a buffer ring of 8
+ buffers which hold the translated string. This is an arbitrary limit
+ but it is approximately double the current needs of this module.
+ */
+LPWSTR
+towide (const char *s, gdb_wince_len * out_len)
+{
+ static int n = -1;
+ static LPWSTR outs[8] =
+ {NULL /*, NULL, etc. */ };
+ gdb_wince_len dummy;
+
+ if (!out_len)
+ out_len = &dummy;
+
+ /* First determine the length required to hold the converted string. */
+ *out_len = sizeof (WCHAR) * MultiByteToWideChar (CP_ACP, 0, s, -1, NULL, 0);
+ if (!*out_len)
+ return NULL; /* The conversion failed */
+
+ if (++n >= (sizeof (outs) / sizeof (outs[0])))
+ n = 0; /* wrap */
+
+ /* Allocate space for the converted string, reusing any previously allocated
+ space, if applicable. Note that if outs[n] is NULL, realloc will act as
+ a malloc (under cygwin, at least).
+ */
+ outs[n] = (LPWSTR) realloc (outs[n], *out_len);
+ memset (outs[n], 0, *out_len);
+ (void) MultiByteToWideChar (CP_ACP, 0, s, -1, outs[n], *out_len);
+ return outs[n];
+}
+
+/******************** Emulation routines start here. ********************
+
+ The functions below are modelled after their Win32 counterparts. They are named
+ similarly to Win32 and take exactly the same arguments except where otherwise noted.
+ They communicate with the stub on the hand-held device by sending their arguments
+ over the socket and waiting for results from the socket.
+
+ There is one universal change. In cases where a length is expected to be returned
+ in a DWORD, we use a gdb_wince_len type instead. Currently this is an unsigned short
+ which is smaller than the standard Win32 DWORD. This is done to minimize unnecessary
+ traffic since the connection to Windows CE can be slow. To change this, modify the
+ typedef in wince-stub.h and change the putlen/getlen macros in this file and in
+ the stub.
+*/
+static int
+create_process (LPSTR exec_file, LPSTR args, DWORD flags, PROCESS_INFORMATION * pi)
+{
+ gdb_wince_len len;
+ LPWSTR buf;
+
+ buf = towide (exec_file, &len);
+ putmemory ("CreateProcess exec_file", GDB_CREATEPROCESS, buf, len);
+ buf = towide (args, &len);
+ putmemory ("CreateProcess args", GDB_CREATEPROCESS, buf, len);
+ putdword ("CreateProcess flags", GDB_CREATEPROCESS, flags);
+ return getresult ("CreateProcess result", GDB_CREATEPROCESS, pi, NULL);
+}
+
+/* Emulate TerminateProcess. Don't bother with the second argument since CE
+ ignores it.
+ */
+static int
+terminate_process (HANDLE h)
+{
+ gdb_wince_result res;
+ if (s < 0)
+ return 1;
+ puthandle ("TerminateProcess handle", GDB_TERMINATEPROCESS, h);
+ return getresult ("TerminateProcess result", GDB_TERMINATEPROCESS, &res, NULL);
+}
+
+static int
+wait_for_debug_event (DEBUG_EVENT * ev, DWORD ms)
+{
+ if (s < 0)
+ return 1;
+ putdword ("WaitForDebugEvent ms", GDB_WAITFORDEBUGEVENT, ms);
+ return getresult ("WaitForDebugEvent event", GDB_WAITFORDEBUGEVENT, ev, NULL);
+}
+
+static int
+get_thread_context (HANDLE h, CONTEXT * c)
+{
+ if (s < 0)
+ return 1;
+ puthandle ("GetThreadContext handle", GDB_GETTHREADCONTEXT, h);
+ putdword ("GetThreadContext flags", GDB_GETTHREADCONTEXT, c->ContextFlags);
+ return getresult ("GetThreadContext context", GDB_GETTHREADCONTEXT, c, NULL);
+}
+
+static int
+set_thread_context (HANDLE h, CONTEXT * c)
+{
+ gdb_wince_result res;
+ if (s < 0)
+ return 1;
+ puthandle ("SetThreadContext handle", GDB_SETTHREADCONTEXT, h);
+ putmemory ("SetThreadContext context", GDB_SETTHREADCONTEXT, c, sizeof (*c));
+ return getresult ("SetThreadContext context", GDB_SETTHREADCONTEXT, &res, NULL);
+}
+
+static int
+read_process_memory (HANDLE h, LPCVOID where, LPVOID buf, gdb_wince_len len, gdb_wince_len * nbytes)
+{
+ if (s < 0)
+ return 1;
+ puthandle ("ReadProcessMemory handle", GDB_READPROCESSMEMORY, h);
+ putpvoid ("ReadProcessMemory location", GDB_READPROCESSMEMORY, where);
+ putlen ("ReadProcessMemory size", GDB_READPROCESSMEMORY, len);
+
+ return getresult ("ReadProcessMemory buf", GDB_READPROCESSMEMORY, buf, nbytes);
+}
+
+static int
+write_process_memory (HANDLE h, LPCVOID where, LPCVOID buf, gdb_wince_len len, gdb_wince_len * nbytes)
+{
+ if (s < 0)
+ return 1;
+ puthandle ("WriteProcessMemory handle", GDB_WRITEPROCESSMEMORY, h);
+ putpvoid ("WriteProcessMemory location", GDB_WRITEPROCESSMEMORY, where);
+ putmemory ("WriteProcProcessMemory buf", GDB_WRITEPROCESSMEMORY, buf, len);
+
+ return getresult ("WriteProcessMemory result", GDB_WRITEPROCESSMEMORY, nbytes, NULL);
+}
+
+static int
+remote_read_bytes (CORE_ADDR memaddr, char *myaddr, int len)
+{
+ gdb_wince_len nbytes;
+ if (!read_process_memory (current_process_handle, (LPCVOID) memaddr,
+ (LPVOID) myaddr, len, &nbytes))
+ return -1;
+ return nbytes;
+}
+
+static int
+remote_write_bytes (CORE_ADDR memaddr, char *myaddr, int len)
+{
+ gdb_wince_len nbytes;
+ if (!write_process_memory (current_process_handle, (LPCVOID) memaddr,
+ (LPCVOID) myaddr, len, &nbytes))
+ return -1;
+ return nbytes;
+}
+
+/* This is not a standard Win32 function. It instructs the stub to return TRUE
+ if the thread referenced by HANDLE h is alive.
+ */
+static int
+thread_alive (HANDLE h)
+{
+ gdb_wince_result res;
+ if (s < 0)
+ return 1;
+ puthandle ("ThreadAlive handle", GDB_THREADALIVE, h);
+ return getresult ("ThreadAlive result", GDB_THREADALIVE, &res, NULL);
+}
+
+static int
+suspend_thread (HANDLE h)
+{
+ if (s < 0)
+ return 1;
+ puthandle ("SuspendThread handle", GDB_SUSPENDTHREAD, h);
+ return (int) getdword ("SuspendThread result", GDB_SUSPENDTHREAD);
+}
+
+static int
+resume_thread (HANDLE h)
+{
+ if (s < 0)
+ return 1;
+ puthandle ("ResumeThread handle", GDB_RESUMETHREAD, h);
+ return (int) getdword ("SuspendThread result", GDB_RESUMETHREAD);
+}
+
+static int
+continue_debug_event (DWORD pid, DWORD tid, DWORD status)
+{
+ gdb_wince_result res;
+ if (s < 0)
+ return 0;
+ putdword ("ContinueDebugEvent pid", GDB_CONTINUEDEBUGEVENT, pid);
+ putdword ("ContinueDebugEvent tid", GDB_CONTINUEDEBUGEVENT, tid);
+ putdword ("ContinueDebugEvent status", GDB_CONTINUEDEBUGEVENT, status);
+ return getresult ("ContinueDebugEvent result", GDB_CONTINUEDEBUGEVENT, &res, NULL);
+}
+
+static int
+close_handle (HANDLE h)
+{
+ gdb_wince_result res;
+ if (s < 0)
+ return 1;
+ puthandle ("CloseHandle handle", GDB_CLOSEHANDLE, h);
+ return (int) getresult ("CloseHandle result", GDB_CLOSEHANDLE, &res, NULL);
+}
+
+/* This is not a standard Win32 interface. This function tells the stub
+ to terminate.
+ */
+static void
+stop_stub ()
+{
+ if (s < 0)
+ return;
+ (void) putdword ("Stopping gdb stub", GDB_STOPSTUB, 0);
+ s = -1;
+}
+
+/******************** End of emulation routines. ********************/
+/******************** End of stub interface ********************/
+
+/* Find a thread record given a thread id.
+ If get_context then also retrieve the context for this
+ thread. */
+static thread_info *
+thread_rec (DWORD id, int get_context)
+{
+ thread_info *th;
+
+ for (th = &thread_head; (th = th->next) != NULL;)
+ if (th->id == id)
+ {
+ if (!th->suspend_count && get_context)
+ {
+ if (get_context > 0 && th != this_thread)
+ th->suspend_count = suspend_thread (th->h) + 1;
+ else if (get_context < 0)
+ th->suspend_count = -1;
+
+ th->context.ContextFlags = CONTEXT_DEBUGGER;
+ get_thread_context (th->h, &th->context);
+ }
+ return th;
+ }
+
+ return NULL;
+}
+
+/* Add a thread to the thread list */
+static thread_info *
+child_add_thread (DWORD id, HANDLE h)
+{
+ thread_info *th;
+
+ if ((th = thread_rec (id, FALSE)))
+ return th;
+
+ th = (thread_info *) xmalloc (sizeof (*th));
+ memset (th, 0, sizeof (*th));
+ th->id = id;
+ th->h = h;
+ th->next = thread_head.next;
+ thread_head.next = th;
+ add_thread (id);
+ return th;
+}
+
+/* Clear out any old thread list and reintialize it to a
+ pristine state. */
+static void
+child_init_thread_list ()
+{
+ thread_info *th = &thread_head;
+
+ DEBUG_EVENTS (("gdb: child_init_thread_list\n"));
+ init_thread_list ();
+ while (th->next != NULL)
+ {
+ thread_info *here = th->next;
+ th->next = here->next;
+ (void) close_handle (here->h);
+ free (here);
+ }
+}
+
+/* Delete a thread from the list of threads */
+static void
+child_delete_thread (DWORD id)
+{
+ thread_info *th;
+
+ if (info_verbose)
+ printf_unfiltered ("[Deleting %s]\n", target_pid_to_str (id));
+ delete_thread (id);
+
+ for (th = &thread_head;
+ th->next != NULL && th->next->id != id;
+ th = th->next)
+ continue;
+
+ if (th->next != NULL)
+ {
+ thread_info *here = th->next;
+ th->next = here->next;
+ close_handle (here->h);
+ free (here);
+ }
+}
+
+static void
+check (BOOL ok, const char *file, int line)
+{
+ if (!ok)
+ printf_filtered ("error return %s:%d was %d\n", file, line, GetLastError ());
+}
+
+/* Return a pointer into a CONTEXT field indexed by gdb register number.
+ Return a pointer to an address pointing to zero if there is no
+ corresponding CONTEXT field for the given register number.
+ */
+static ULONG *
+regptr (LPCONTEXT c, int r)
+{
+ static ULONG zero = 0;
+ ULONG *p;
+ if (mappings[r] < 0)
+ p = &zero;
+ else
+ p = (ULONG *) (((char *) c) + mappings[r]);
+ return p;
+}
+
+static void
+do_child_fetch_inferior_registers (int r)
+{
+ if (r >= 0)
+ {
+ supply_register (r, (char *) regptr (&current_thread->context, r));
+ }
+ else
+ {
+ for (r = 0; r < NUM_REGS; r++)
+ do_child_fetch_inferior_registers (r);
+ }
+}
+
+static void
+child_fetch_inferior_registers (int r)
+{
+ current_thread = thread_rec (inferior_pid, TRUE);
+ do_child_fetch_inferior_registers (r);
+}
+
+static void
+do_child_store_inferior_registers (int r)
+{
+ if (r >= 0)
+ read_register_gen (r, ((char *) &current_thread->context) + mappings[r]);
+ else
+ {
+ for (r = 0; r < NUM_REGS; r++)
+ do_child_store_inferior_registers (r);
+ }
+}
+
+/* Store a new register value into the current thread context */
+static void
+child_store_inferior_registers (int r)
+{
+ current_thread = thread_rec (inferior_pid, TRUE);
+ do_child_store_inferior_registers (r);
+}
+
+/* Wait for child to do something. Return pid of child, or -1 in case
+ of error; store status through argument pointer OURSTATUS. */
+
+static int
+handle_load_dll (PTR dummy)
+{
+ LOAD_DLL_DEBUG_INFO *event = &current_event.u.LoadDll;
+ char dll_buf[MAX_PATH + 1];
+ char *p, *bufp, *imgp, *dll_name, *dll_basename;
+ int len;
+
+ dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
+ if (!event->lpImageName)
+ return 1;
+
+ len = 0;
+ for (bufp = dll_buf, imgp = event->lpImageName;
+ bufp < dll_buf + sizeof (dll_buf);
+ bufp += 16, imgp += 16)
+ {
+ gdb_wince_len nbytes = 0;
+ (void) read_process_memory (current_process_handle,
+ imgp, bufp, 16, &nbytes);
+
+ if (!nbytes && bufp == dll_buf)
+ return 1; /* couldn't read it */
+ for (p = bufp; p < bufp + nbytes; p++)
+ {
+ len++;
+ if (*p == '\0')
+ goto out;
+ if (event->fUnicode)
+ p++;
+ }
+ if (!nbytes)
+ break;
+ }
+
+out:
+ if (!len)
+ return 1;
+ dll_buf[len] = '\0';
+ dll_name = alloca (len);
+
+ if (!dll_name)
+ return 1;
+
+ if (!event->fUnicode)
+ memcpy (dll_name, dll_buf, len);
+ else
+ WideCharToMultiByte (CP_ACP, 0, (LPCWSTR) dll_buf, len,
+ dll_name, len, 0, 0);
+
+ while ((p = strchr (dll_name, '\\')))
+ *p = '/';
+
+ /* FIXME!! It would be nice to define one symbol which pointed to the
+ front of the dll if we can't find any symbols. */
+
+ if (!(dll_basename = strrchr (dll_name, '/')))
+ dll_basename = dll_name;
+ else
+ dll_basename++;
+
+ /* The symbols in a dll are offset by 0x1000, which is the
+ the offset from 0 of the first byte in an image - because
+ of the file header and the section alignment.
+
+ FIXME: Is this the real reason that we need the 0x1000 ? */
+
+ printf_unfiltered ("%x:%s", event->lpBaseOfDll, dll_name);
+#if 0 /* FIXME: Need to use RAPI stuff to read the file someday. */
+ {
+ struct section_addr_info section_addrs;
+ memset (&section_addrs, 0, sizeof (section_addrs));
+ section_addrs.text_addr = (int) event->lpBaseOfDll + 0x1000;
+ symbol_file_add (dll_name, 0, &section_addrs, 0, OBJF_SHARED);
+ }
+#endif
+ printf_unfiltered ("\n");
+
+ return 1;
+}
+
+/* Handle DEBUG_STRING output from child process.
+ Cygwin prepends its messages with a "cygwin:". Interpret this as
+ a Cygwin signal. Otherwise just print the string as a warning. */
+static void
+handle_output_debug_string (struct target_waitstatus *ourstatus)
+{
+ char p[256];
+ char s[255];
+ char *q;
+ gdb_wince_len nbytes_read;
+ gdb_wince_len nbytes = current_event.u.DebugString.nDebugStringLength;
+
+ if (nbytes > 255)
+ nbytes = 255;
+
+ memset (p, 0, sizeof (p));
+ if (!read_process_memory (current_process_handle,
+ current_event.u.DebugString.lpDebugStringData,
+ &p, nbytes, &nbytes_read)
+ || !*p)
+ return;
+
+ memset (s, 0, sizeof (s));
+ WideCharToMultiByte (CP_ACP, 0, (LPCWSTR) p, (int) nbytes_read, s,
+ sizeof (s) - 1, NULL, NULL);
+ q = strchr (s, '\n');
+ if (q != NULL)
+ {
+ *q = '\0';
+ if (*--q = '\r')
+ *q = '\0';
+ }
+
+ warning (s);
+ return;
+}
+
+/* Handle target exceptions. */
+static int
+handle_exception (struct target_waitstatus *ourstatus)
+{
+ thread_info *th;
+
+ if (current_event.u.Exception.dwFirstChance)
+ return 0;
+
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ /* Record the context of the current thread */
+ th = thread_rec (current_event.dwThreadId, -1);
+
+ switch (current_event.u.Exception.ExceptionRecord.ExceptionCode)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ DEBUG_EXCEPT (("gdb: Target exception ACCESS_VIOLATION at 0x%08x\n",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+ ourstatus->value.sig = TARGET_SIGNAL_SEGV;
+ break;
+ case STATUS_STACK_OVERFLOW:
+ DEBUG_EXCEPT (("gdb: Target exception STACK_OVERFLOW at 0x%08x\n",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+ ourstatus->value.sig = TARGET_SIGNAL_SEGV;
+ break;
+ case EXCEPTION_BREAKPOINT:
+ DEBUG_EXCEPT (("gdb: Target exception BREAKPOINT at 0x%08x\n",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+ ourstatus->value.sig = TARGET_SIGNAL_TRAP;
+ break;
+ case DBG_CONTROL_C:
+ DEBUG_EXCEPT (("gdb: Target exception CONTROL_C at 0x%08x\n",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+ ourstatus->value.sig = TARGET_SIGNAL_INT;
+ /* User typed CTRL-C. Continue with this status */
+ last_sig = SIGINT; /* FIXME - should check pass state */
+ break;
+ case EXCEPTION_SINGLE_STEP:
+ DEBUG_EXCEPT (("gdb: Target exception SINGLE_STEP at 0x%08x\n",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionAddress));
+ ourstatus->value.sig = TARGET_SIGNAL_TRAP;
+ break;
+ default:
+ /* This may be a structured exception handling exception. In
+ that case, we want to let the program try to handle it, and
+ only break if we see the exception a second time. */
+
+ printf_unfiltered ("gdb: unknown target exception 0x%08x at 0x%08x\n",
+ current_event.u.Exception.ExceptionRecord.ExceptionCode,
+ current_event.u.Exception.ExceptionRecord.ExceptionAddress);
+ ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+ break;
+ }
+ exception_count++;
+ return 1;
+}
+
+/* Resume all artificially suspended threads if we are continuing
+ execution */
+static BOOL
+child_continue (DWORD continue_status, int id)
+{
+ int i;
+ thread_info *th;
+ BOOL res;
+
+ DEBUG_EVENTS (("ContinueDebugEvent (cpid=%d, ctid=%d, DBG_CONTINUE);\n",
+ (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId));
+ res = continue_debug_event (current_event.dwProcessId,
+ current_event.dwThreadId,
+ continue_status);
+ if (res)
+ for (th = &thread_head; (th = th->next) != NULL;)
+ if (((id == -1) || (id == th->id)) && th->suspend_count)
+ {
+ for (i = 0; i < th->suspend_count; i++)
+ (void) resume_thread (th->h);
+ th->suspend_count = 0;
+ }
+
+ return res;
+}
+
+/* Get the next event from the child. Return 1 if the event requires
+ handling by WFI (or whatever).
+ */
+static int
+get_child_debug_event (int pid, struct target_waitstatus *ourstatus, DWORD * event_code, int *retval)
+{
+ BOOL debug_event;
+ DWORD continue_status;
+ int breakout = 1;
+
+ if (!(debug_event = wait_for_debug_event (&current_event, 1000)))
+ {
+ breakout = *retval = *event_code = 0;
+ goto out;
+ }
+
+ this_thread = thread_rec (current_event.dwThreadId, FALSE);
+ event_count++;
+ continue_status = DBG_CONTINUE;
+ *retval = 0;
+ switch (*event_code = current_event.dwDebugEventCode)
+ {
+ case CREATE_THREAD_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%x code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "CREATE_THREAD_DEBUG_EVENT"));
+ /* Record the existence of this thread */
+ child_add_thread (current_event.dwThreadId,
+ current_event.u.CreateThread.hThread);
+ if (info_verbose)
+ printf_unfiltered ("[New %s]\n",
+ target_pid_to_str (current_event.dwThreadId));
+ break;
+
+ case EXIT_THREAD_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "EXIT_THREAD_DEBUG_EVENT"));
+ child_delete_thread (current_event.dwThreadId);
+ break;
+
+ case CREATE_PROCESS_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "CREATE_PROCESS_DEBUG_EVENT"));
+ current_process_handle = current_event.u.CreateProcessInfo.hProcess;
+
+ main_thread_id = inferior_pid = current_event.dwThreadId;
+ /* Add the main thread */
+ current_thread = child_add_thread (inferior_pid,
+ current_event.u.CreateProcessInfo.hThread);
+ break;
+
+ case EXIT_PROCESS_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "EXIT_PROCESS_DEBUG_EVENT"));
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
+ close_handle (current_process_handle);
+ *retval = current_event.dwProcessId;
+ goto out;
+
+ case LOAD_DLL_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "LOAD_DLL_DEBUG_EVENT"));
+ catch_errors (handle_load_dll, NULL, (char *) "", RETURN_MASK_ALL);
+ registers_changed (); /* mark all regs invalid */
+ break;
+
+ case UNLOAD_DLL_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "UNLOAD_DLL_DEBUG_EVENT"));
+ break; /* FIXME: don't know what to do here */
+
+ case EXCEPTION_DEBUG_EVENT:
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "EXCEPTION_DEBUG_EVENT"));
+ if (handle_exception (ourstatus))
+ {
+ char buf[32];
+ *retval = current_event.dwThreadId;
+ remote_read_bytes (read_pc (), buf, sizeof (buf));
+ dcache_xfer_memory (remote_dcache, read_pc (), buf, sizeof (buf), 0);
+ goto out;
+ }
+ continue_status = DBG_EXCEPTION_NOT_HANDLED;
+ break;
+
+ case OUTPUT_DEBUG_STRING_EVENT: /* message from the kernel */
+ DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ "OUTPUT_DEBUG_STRING_EVENT"));
+ handle_output_debug_string (ourstatus);
+ break;
+ default:
+ printf_unfiltered ("gdb: kernel event for pid=%d tid=%d\n",
+ current_event.dwProcessId,
+ current_event.dwThreadId);
+ printf_unfiltered (" unknown event code %d\n",
+ current_event.dwDebugEventCode);
+ break;
+ }
+
+ breakout = 0;
+ CHECK (child_continue (continue_status, -1));
+
+out:
+ return breakout;
+}
+
+/* Wait for interesting events to occur in the target process. */
+static int
+child_wait (int pid, struct target_waitstatus *ourstatus)
+{
+ DWORD event_code;
+ int retval;
+
+ /* We loop when we get a non-standard exception rather than return
+ with a SPURIOUS because resume can try and step or modify things,
+ which needs a current_thread->h. But some of these exceptions mark
+ the birth or death of threads, which mean that the current thread
+ isn't necessarily what you think it is. */
+
+ while (1)
+ if (get_child_debug_event (pid, ourstatus, &event_code, &retval))
+ return retval;
+ else
+ {
+ int detach = 0;
+
+ if (ui_loop_hook != NULL)
+ detach = ui_loop_hook (0);
+
+ if (detach)
+ child_kill_inferior ();
+ }
+}
+
+/* Print status information about what we're accessing. */
+
+static void
+child_files_info (ignore)
+ struct target_ops *ignore;
+{
+ printf_unfiltered ("\tUsing the running image of child %s.\n",
+ target_pid_to_str (inferior_pid));
+}
+
+/* ARGSUSED */
+static void
+child_open (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ error ("Use the \"run\" command to start a child process.");
+}
+
+#define FACTOR (0x19db1ded53ea710LL)
+#define NSPERSEC 10000000
+
+/* Convert a Win32 time to "UNIX" format. */
+long
+to_time_t (FILETIME * ptr)
+{
+ /* A file time is the number of 100ns since jan 1 1601
+ stuffed into two long words.
+ A time_t is the number of seconds since jan 1 1970. */
+
+ long rem;
+ long long x = ((long long) ptr->dwHighDateTime << 32) + ((unsigned) ptr->dwLowDateTime);
+ x -= FACTOR; /* number of 100ns between 1601 and 1970 */
+ rem = x % ((long long) NSPERSEC);
+ rem += (NSPERSEC / 2);
+ x /= (long long) NSPERSEC; /* number of 100ns in a second */
+ x += (long long) (rem / NSPERSEC);
+ return x;
+}
+
+/* Upload a file to the remote device depending on the user's
+ 'set remoteupload' specification. */
+char *
+upload_to_device (const char *to, const char *from)
+{
+ HANDLE h;
+ const char *dir = remote_directory ? : "\\gdb";
+ int len;
+ static char *remotefile = NULL;
+ LPWSTR wstr;
+ char *p;
+ DWORD err;
+ const char *in_to = to;
+ FILETIME ctime, atime, wtime;
+ struct stat st;
+ int fd;
+
+ /* Look for a path separator and only use trailing part. */
+ while ((p = strpbrk (to, "/\\")) != NULL)
+ to = p + 1;
+
+ if (!*to)
+ error ("no filename found to upload - %s.", in_to);
+
+ len = strlen (dir) + strlen (to) + 2;
+ remotefile = (char *) realloc (remotefile, len);
+ strcpy (remotefile, dir);
+ strcat (remotefile, "\\");
+ strcat (remotefile, to);
+
+ if (upload_when == UPLOAD_NEVER)
+ return remotefile; /* Don't bother uploading. */
+
+ /* Open the source. */
+ if ((fd = openp (getenv ("PATH"), TRUE, (char *) from, O_RDONLY, 0, NULL)) < 0)
+ error ("couldn't open %s", from);
+
+ /* Get the time for later comparison. */
+ if (fstat (fd, &st))
+ st.st_mtime = (time_t) - 1;
+
+ /* Always attempt to create the directory on the remote system. */
+ wstr = towide (dir, NULL);
+ (void) CeCreateDirectory (wstr, NULL);
+
+ /* Attempt to open the remote file, creating it if it doesn't exist. */
+ wstr = towide (remotefile, NULL);
+ h = CeCreateFile (wstr, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ /* Some kind of problem? */
+ err = CeGetLastError ();
+ if (h == NULL)
+ error ("error creating file to \"%s\". Windows error %d.",
+ remotefile, err);
+
+ /* See if we need to upload the file. */
+ if (upload_when == UPLOAD_ALWAYS ||
+ err != ERROR_ALREADY_EXISTS ||
+ !CeGetFileTime (h, &ctime, &atime, &wtime) ||
+ to_time_t (&wtime) < st.st_mtime)
+ {
+ DWORD nbytes;
+ char buf[4096];
+ int n;
+
+ /* Upload the file. */
+ while ((n = read (fd, buf, sizeof (buf))) > 0)
+ if (!CeWriteFile (h, buf, (DWORD) n, &nbytes, NULL))
+ error ("error writing to remote device - %d.",
+ CeGetLastError ());
+ }
+
+ close (fd);
+ CeCloseHandle (h);
+
+ return remotefile;
+}
+
+/* Initialize the connection to the remote device. */
+static void
+wince_initialize ()
+{
+ int tmp;
+ char args[256];
+ char *hostname;
+ struct sockaddr_in sin;
+ char *stub_file_name;
+ int s0;
+ PROCESS_INFORMATION pi;
+
+ if (!connection_initialized)
+ switch (CeRapiInit ())
+ {
+ case 0:
+ connection_initialized = 1;
+ break;
+ default:
+ CeRapiUninit ();
+ error ("Can't initialize connection to remote device.\n");
+ break;
+ }
+
+ /* Upload the stub to the handheld device. */
+ stub_file_name = upload_to_device ("wince-stub.exe", WINCE_STUB);
+ strcpy (args, stub_file_name);
+
+ if (remote_add_host)
+ {
+ strcat (args, " ");
+ hostname = strchr (args, '\0');
+ if (gethostname (hostname, sizeof (args) - strlen (args)))
+ error ("couldn't get hostname of this system.");
+ }
+
+ /* Get a socket. */
+ if ((s0 = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ stub_error ("Couldn't connect to host system.");
+
+ /* Allow rapid reuse of the port. */
+ tmp = 1;
+ (void) setsockopt (s0, 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 = AF_INET;
+ sin.sin_port = htons (7000); /* FIXME: This should be configurable */
+
+ if (bind (s0, (struct sockaddr *) &sin, sizeof (sin)))
+ error ("couldn't bind socket");
+
+ if (listen (s0, 1))
+ error ("Couldn't open socket for listening.\n");
+
+ /* Start up the stub on the remote device. */
+ if (!CeCreateProcess (towide (stub_file_name, NULL), towide (args, NULL),
+ NULL, NULL, 0, 0, NULL, NULL, NULL, &pi))
+ error ("Unable to start remote stub '%s'. Windows CE error %d.",
+ stub_file_name, CeGetLastError ());
+
+ /* Wait for a connection */
+
+ if ((s = accept (s0, NULL, NULL)) < 0)
+ error ("couldn't set up server for connection.");
+
+ close (s0);
+}
+
+/* Start an inferior win32 child process and sets inferior_pid to its pid.
+ EXEC_FILE is the file to run.
+ ALLARGS is a string containing the arguments to the program.
+ ENV is the environment vector to pass. Errors reported with error(). */
+static void
+child_create_inferior (char *exec_file, char *args, char **env)
+{
+ PROCESS_INFORMATION pi;
+ struct target_waitstatus dummy;
+ int ret;
+ DWORD flags, event_code;
+ char *exec_and_args;
+
+ if (!exec_file)
+ error ("No executable specified, use `target exec'.\n");
+
+ flags = DEBUG_PROCESS;
+
+ wince_initialize (); /* Make sure we've got a connection. */
+ if (!remote_dcache)
+ remote_dcache = dcache_init (remote_read_bytes, remote_write_bytes);
+ else
+ dcache_flush (remote_dcache);
+
+ exec_file = upload_to_device (exec_file, exec_file);
+
+ while (*args == ' ')
+ args++;
+
+ /* Allocate space for "command<sp>args" */
+ if (*args == '\0')
+ {
+ exec_and_args = alloca (strlen (exec_file) + 1);
+ strcpy (exec_and_args, exec_file);
+ }
+ else
+ {
+ exec_and_args = alloca (strlen (exec_file + strlen (args) + 2));
+ sprintf (exec_and_args, "%s %s", exec_file, args);
+ }
+
+ memset (&pi, 0, sizeof (pi));
+ /* Execute the process */
+ if (!create_process (exec_file, exec_and_args, flags, &pi))
+ error ("Error creating process %s, (error %d)\n", exec_file, GetLastError ());
+
+ exception_count = 0;
+ event_count = 0;
+
+ current_process_handle = pi.hProcess;
+ current_event.dwProcessId = pi.dwProcessId;
+ memset (&current_event, 0, sizeof (current_event));
+ inferior_pid = current_event.dwThreadId = pi.dwThreadId;
+ push_target (&child_ops);
+ child_init_thread_list ();
+ child_add_thread (pi.dwThreadId, pi.hThread);
+ init_wait_for_inferior ();
+ clear_proceed_status ();
+ target_terminal_init ();
+ target_terminal_inferior ();
+
+
+ /* Run until process and threads are loaded */
+ do
+ get_child_debug_event (inferior_pid, &dummy, &event_code, &ret);
+ while (event_code != CREATE_PROCESS_DEBUG_EVENT);
+
+ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0);
+}
+
+/* Chile has gone bye-bye. */
+static void
+child_mourn_inferior ()
+{
+ (void) child_continue (DBG_CONTINUE, -1);
+ unpush_target (&child_ops);
+ stop_stub ();
+ CeRapiUninit ();
+ connection_initialized = 0;
+ generic_mourn_inferior ();
+}
+
+/* Move memory from child to/from gdb. */
+int
+child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
+ int write, struct target_ops *target)
+{
+ if (len <= 0)
+ return 0;
+ return dcache_xfer_memory (remote_dcache, memaddr, our, len, write);
+}
+
+/* Terminate the process and wait for child to tell us it has completed. */
+void
+child_kill_inferior (void)
+{
+ CHECK (terminate_process (current_process_handle));
+
+ for (;;)
+ {
+ if (!child_continue (DBG_CONTINUE, -1))
+ break;
+ if (!wait_for_debug_event (&current_event, INFINITE))
+ break;
+ if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
+ break;
+ }
+
+ CHECK (close_handle (current_process_handle));
+ close_handle (current_thread->h);
+ target_mourn_inferior (); /* or just child_mourn_inferior? */
+}
+
+#ifdef MIPS
+static void
+undoSStep (thread_info * th)
+{
+ if (th->stepped)
+ {
+ memory_remove_breakpoint (th->step_pc, (void *) &th->step_prev);
+ th->stepped = 0;
+ }
+}
+
+void
+wince_software_single_step (unsigned int ignore, int insert_breakpoints_p)
+{
+ unsigned long pc;
+ thread_info *th = current_thread; /* Info on currently selected thread */
+ CORE_ADDR mips_next_pc (CORE_ADDR pc);
+
+ if (!insert_breakpoints_p)
+ {
+ undoSStep (th);
+ return;
+ }
+
+ th->stepped = 1;
+ pc = read_register (PC_REGNUM);
+ th->step_pc = mips_next_pc (pc);
+ th->step_prev = 0;
+ memory_insert_breakpoint (th->step_pc, (void *) &th->step_prev);
+}
+#elif SHx
+/* Hitachi SH architecture instruction encoding masks */
+
+#define COND_BR_MASK 0xff00
+#define UCOND_DBR_MASK 0xe000
+#define UCOND_RBR_MASK 0xf0df
+#define TRAPA_MASK 0xff00
+
+#define COND_DISP 0x00ff
+#define UCOND_DISP 0x0fff
+#define UCOND_REG 0x0f00
+
+/* Hitachi SH instruction opcodes */
+
+#define BF_INSTR 0x8b00
+#define BT_INSTR 0x8900
+#define BRA_INSTR 0xa000
+#define BSR_INSTR 0xb000
+#define JMP_INSTR 0x402b
+#define JSR_INSTR 0x400b
+#define RTS_INSTR 0x000b
+#define RTE_INSTR 0x002b
+#define TRAPA_INSTR 0xc300
+#define SSTEP_INSTR 0xc3ff
+
+
+#define T_BIT_MASK 0x0001
+
+/* Undo the effect of a previous doSStep. If we single stepped,
+ restore the old instruction. */
+
+static void
+undoSStep (thread_info * th)
+{
+ if (th->stepped)
+ {
+ gdb_wince_len done;
+ write_process_memory (current_process_handle, (LPVOID) th->step_pc,
+ (LPVOID) & th->step_instr, sizeof (short), &done);
+ if (done != sizeof (short))
+ error ("error unsetting single step.");
+ th->stepped = 0;
+ }
+}
+
+/* Single step (in a painstaking fashion) by inspecting the current
+ instruction and setting a breakpoint on the "next" instruction
+ which would be executed. This code hails from sh-stub.c.
+ */
+void
+wince_software_single_step (unsigned int ignore, int insert_breakpoints_p)
+{
+ thread_info *th = current_thread; /* Info on currently selected thread */
+
+ if (!insert_breakpoints_p)
+ undoSStep (th);
+ else
+ {
+ short *instrMem;
+ int displacement;
+ int reg;
+ unsigned short opcode;
+ gdb_wince_len done;
+ LPCONTEXT c = &th->context;
+
+ instrMem = (short *) c->Fir;
+
+ read_process_memory (current_process_handle, (LPCVOID) c->Fir, &opcode,
+ sizeof (opcode), &done);
+ if (done != sizeof (opcode))
+ error ("couldn't retrieve opcode");
+ th->stepped = 1;
+
+ if ((opcode & COND_BR_MASK) == BT_INSTR)
+ {
+ if (c->Psr & T_BIT_MASK)
+ {
+ displacement = (opcode & COND_DISP) << 1;
+ if (displacement & 0x80)
+ displacement |= 0xffffff00;
+ /*
+ * Remember PC points to second instr.
+ * after PC of branch ... so add 4
+ */
+ instrMem = (short *) (c->Fir + displacement + 4);
+ }
+ else
+ instrMem += 1;
+ }
+ else if ((opcode & COND_BR_MASK) == BF_INSTR)
+ {
+ if (c->Psr & T_BIT_MASK)
+ instrMem += 1;
+ else
+ {
+ displacement = (opcode & COND_DISP) << 1;
+ if (displacement & 0x80)
+ displacement |= 0xffffff00;
+ /*
+ * Remember PC points to second instr.
+ * after PC of branch ... so add 4
+ */
+ instrMem = (short *) (c->Fir + displacement + 4);
+ }
+ }
+ else if ((opcode & UCOND_DBR_MASK) == BRA_INSTR)
+ {
+ displacement = (opcode & UCOND_DISP) << 1;
+ if (displacement & 0x0800)
+ displacement |= 0xfffff000;
+
+ /*
+ * Remember PC points to second instr.
+ * after PC of branch ... so add 4
+ */
+ instrMem = (short *) (c->Fir + displacement + 4);
+ }
+ else if ((opcode & UCOND_RBR_MASK) == JSR_INSTR)
+ {
+ reg = (char) ((opcode & UCOND_REG) >> 8);
+
+ instrMem = (short *) *regptr (c, reg);
+ }
+ else if (opcode == RTS_INSTR)
+ instrMem = (short *) c->PR;
+ else if (opcode == RTE_INSTR)
+ instrMem = (short *) *regptr (c, 15);
+ else if ((opcode & TRAPA_MASK) == TRAPA_INSTR)
+ instrMem = (short *) ((opcode & ~TRAPA_MASK) << 2);
+ else
+ instrMem += 1;
+
+ th->step_pc = (CORE_ADDR) instrMem;
+
+ read_process_memory (current_process_handle, (LPVOID) instrMem,
+ (LPVOID) & th->step_instr, sizeof (short), &done);
+ opcode = SSTEP_INSTR;
+ write_process_memory (current_process_handle, (LPVOID) instrMem,
+ (LPVOID) & opcode, sizeof (short), &done);
+ }
+}
+#elif defined (ARM)
+/* Single step (in a painstaking fashion) by inspecting the current
+ instruction and setting a breakpoint on the "next" instruction
+ which would be executed. This code hails from sh-stub.c.
+ */
+static void
+undoSStep (thread_info * th)
+{
+ if (th->stepped)
+ {
+ memory_remove_breakpoint (th->step_pc, (void *) &th->step_prev);
+ th->stepped = 0;
+ }
+}
+
+void
+wince_software_single_step (unsigned int ignore, int insert_breakpoints_p)
+{
+ unsigned long pc;
+ thread_info *th = current_thread; /* Info on currently selected thread */
+ CORE_ADDR mips_next_pc (CORE_ADDR pc);
+
+ if (!insert_breakpoints_p)
+ {
+ undoSStep (th);
+ return;
+ }
+
+ th->stepped = 1;
+ pc = read_register (PC_REGNUM);
+ th->step_pc = arm_get_next_pc (pc);
+ th->step_prev = 0;
+ memory_insert_breakpoint (th->step_pc, (void *) &th->step_prev);
+}
+#endif
+
+/* Resume the child after an exception. */
+void
+child_resume (int pid, int step, enum target_signal sig)
+{
+ thread_info *th;
+ DWORD continue_status = last_sig > 0 && last_sig < NSIG ?
+ DBG_EXCEPTION_NOT_HANDLED : DBG_CONTINUE;
+
+ DEBUG_EXEC (("gdb: child_resume (pid=%d, step=%d, sig=%d);\n",
+ pid, step, sig));
+
+ /* Get context for currently selected thread */
+ th = thread_rec (current_event.dwThreadId, FALSE);
+
+ if (th->context.ContextFlags)
+ {
+ CHECK (set_thread_context (th->h, &th->context));
+ th->context.ContextFlags = 0;
+ }
+
+ dcache_flush (remote_dcache);
+
+ /* Allow continuing with the same signal that interrupted us.
+ Otherwise complain. */
+ if (sig && sig != last_sig)
+ fprintf_unfiltered (gdb_stderr, "Can't send signals to the child. signal %d\n", sig);
+
+ last_sig = 0;
+ child_continue (continue_status, pid);
+}
+
+static void
+child_prepare_to_store ()
+{
+ /* Do nothing, since we can store individual regs */
+}
+
+static int
+child_can_run ()
+{
+ return 1;
+}
+
+static void
+child_close ()
+{
+ DEBUG_EVENTS (("gdb: child_close, inferior_pid=%d\n", inferior_pid));
+}
+
+/* Explicitly upload file to remotedir */
+
+static void
+child_load (char *file, int from_tty)
+{
+ upload_to_device (file, file);
+}
+
+struct target_ops child_ops;
+
+static void
+init_child_ops (void)
+{
+ memset (&child_ops, 0, sizeof (child_ops));
+ child_ops.to_shortname = (char *) "child";
+ child_ops.to_longname = (char *) "Windows CE process";
+ child_ops.to_doc = (char *) "Windows CE process (started by the \"run\" command).";
+ child_ops.to_open = child_open;
+ child_ops.to_close = child_close;
+ child_ops.to_resume = child_resume;
+ child_ops.to_wait = child_wait;
+ child_ops.to_fetch_registers = child_fetch_inferior_registers;
+ child_ops.to_store_registers = child_store_inferior_registers;
+ child_ops.to_prepare_to_store = child_prepare_to_store;
+ child_ops.to_xfer_memory = child_xfer_memory;
+ child_ops.to_files_info = child_files_info;
+ child_ops.to_insert_breakpoint = memory_insert_breakpoint;
+ child_ops.to_remove_breakpoint = memory_remove_breakpoint;
+ child_ops.to_terminal_init = terminal_init_inferior;
+ child_ops.to_terminal_inferior = terminal_inferior;
+ child_ops.to_terminal_ours_for_output = terminal_ours_for_output;
+ child_ops.to_terminal_ours = terminal_ours;
+ child_ops.to_terminal_info = child_terminal_info;
+ child_ops.to_kill = child_kill_inferior;
+ child_ops.to_load = child_load;
+ child_ops.to_create_inferior = child_create_inferior;
+ child_ops.to_mourn_inferior = child_mourn_inferior;
+ child_ops.to_can_run = child_can_run;
+ child_ops.to_thread_alive = win32_child_thread_alive;
+ child_ops.to_stratum = process_stratum;
+ child_ops.to_has_all_memory = 1;
+ child_ops.to_has_memory = 1;
+ child_ops.to_has_stack = 1;
+ child_ops.to_has_registers = 1;
+ child_ops.to_has_execution = 1;
+ child_ops.to_sections = 0;
+ child_ops.to_sections_end = 0;
+ child_ops.to_magic = OPS_MAGIC;
+}
+
+
+/* Handle 'set remoteupload' parameter. */
+
+#define replace_upload(what) \
+ upload_when = UPLOAD_NEWER; \
+ remote_upload = realloc (remote_upload, strlen (upload_options[upload_when].name)); \
+ strcpy (remote_upload, upload_options[upload_when].name);
+
+static void
+set_upload_type (char *ignore, int from_tty)
+{
+ int i, len;
+ char *bad_option;
+
+ if (!remote_upload || !remote_upload[0])
+ {
+ replace_upload (UPLOAD_NEWER);
+ if (from_tty)
+ printf_unfiltered ("Upload upload_options are: always, newer, never.\n");
+ return;
+ }
+
+ len = strlen (remote_upload);
+ for (i = 0; i < (sizeof (upload_options) / sizeof (upload_options[0])); i++)
+ if (len >= upload_options[i].abbrev &&
+ strncasecmp (remote_upload, upload_options[i].name, len) == 0)
+ {
+ remote_upload = (char *) upload_options[i].name;
+ upload_when = i;
+ return;
+ }
+
+ bad_option = remote_upload;
+ replace_upload (UPLOAD_NEWER);
+ error ("Unknown upload type: %s.", bad_option);
+}
+
+void
+_initialize_inftarg ()
+{
+ struct cmd_list_element *set;
+ init_child_ops ();
+
+ add_show_from_set
+ (add_set_cmd ((char *) "remotedirectory", no_class,
+ var_string_noescape, (char *) &remote_directory,
+ (char *) "Set directory for remote upload.\n",
+ &setlist),
+ &showlist);
+ remote_directory = xstrdup (remote_directory);
+
+ set = add_set_cmd ((char *) "remoteupload", no_class,
+ var_string_noescape, (char *) &remote_upload,
+ (char *) "Set how to upload executables to remote device.\n",
+ &setlist);
+ add_show_from_set (set, &showlist);
+ set->function.cfunc = set_upload_type;
+ set_upload_type (NULL, 0);
+ set_dcache_state (1);
+
+ add_show_from_set
+ (add_set_cmd ((char *) "debugexec", class_support, var_boolean,
+ (char *) &debug_exec,
+ (char *) "Set whether to display execution in child process.",
+ &setlist),
+ &showlist);
+
+ add_show_from_set
+ (add_set_cmd ((char *) "remoteaddhost", class_support, var_boolean,
+ (char *) &remote_add_host,
+ (char *) "Set whether to add this host to remote stub arguments for\n
+debugging over a network.", &setlist),
+ &showlist);
+
+ add_show_from_set
+ (add_set_cmd ((char *) "debugevents", class_support, var_boolean,
+ (char *) &debug_events,
+ (char *) "Set whether to display kernel events in child process.",
+ &setlist),
+ &showlist);
+
+ add_show_from_set
+ (add_set_cmd ((char *) "debugmemory", class_support, var_boolean,
+ (char *) &debug_memory,
+ (char *) "Set whether to display memory accesses in child process.",
+ &setlist),
+ &showlist);
+
+ add_show_from_set
+ (add_set_cmd ((char *) "debugexceptions", class_support, var_boolean,
+ (char *) &debug_exceptions,
+ (char *) "Set whether to display kernel exceptions in child process.",
+ &setlist),
+ &showlist);
+
+ add_target (&child_ops);
+}
+
+/* Determine if the thread referenced by "pid" is alive
+ by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0
+ it means that the pid has died. Otherwise it is assumed to be alive. */
+static int
+win32_child_thread_alive (int pid)
+{
+ return thread_alive (thread_rec (pid, FALSE)->h);
+}
+
+/* Convert pid to printable format. */
+char *
+cygwin_pid_to_str (int pid)
+{
+ static char buf[80];
+ if (pid == current_event.dwProcessId)
+ sprintf (buf, "process %d", pid);
+ else
+ sprintf (buf, "thread %d.0x%x", (unsigned) current_event.dwProcessId, pid);
+ return buf;
+}