1/* wince-stub.c -- debugging stub for a Windows CE device 2 3 Copyright 1999, 2000 Free Software Foundation, Inc. 4 Contributed by Cygnus Solutions, A Red Hat Company. 5 6 This file is part of GDB. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without eve nthe implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place - Suite 330, 21 Boston, MA 02111-1307, USA. 22 */ 23 24/* by Christopher Faylor (cgf@cygnus.com) */ 25 26#include <stdarg.h> 27#include <windows.h> 28#include <winsock.h> 29#include "wince-stub.h" 30 31#define MALLOC(n) (void *) LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, (UINT)(n)) 32#define REALLOC(s, n) (void *) LocalReAlloc ((HLOCAL)(s), (UINT)(n), LMEM_MOVEABLE) 33#define FREE(s) LocalFree ((HLOCAL)(s)) 34 35static int skip_next_id = 0; /* Don't read next API code from socket */ 36 37/* v-style interface for handling varying argument list error messages. 38 Displays the error message in a dialog box and exits when user clicks 39 on OK. */ 40static void 41vstub_error (LPCWSTR fmt, va_list args) 42{ 43 WCHAR buf[4096]; 44 wvsprintfW (buf, fmt, args); 45 46 MessageBoxW (NULL, buf, L"GDB", MB_ICONERROR); 47 WSACleanup (); 48 ExitThread (1); 49} 50 51/* The standard way to display an error message and exit. */ 52static void 53stub_error (LPCWSTR fmt, ...) 54{ 55 va_list args; 56 va_start (args, fmt); 57 vstub_error (fmt, args); 58} 59 60/* Allocate a limited pool of memory, reallocating over unused 61 buffers. This assumes that there will never be more than four 62 "buffers" required which, so far, is a safe assumption. */ 63static LPVOID 64mempool (unsigned int len) 65{ 66 static int outn = -1; 67 static LPWSTR outs[4] = {NULL, NULL, NULL, NULL}; 68 69 if (++outn >= (sizeof (outs) / sizeof (outs[0]))) 70 outn = 0; 71 72 /* Allocate space for the converted string, reusing any previously allocated 73 space, if applicable. */ 74 if (outs[outn]) 75 FREE (outs[outn]); 76 outs[outn] = (LPWSTR) MALLOC (len); 77 78 return outs[outn]; 79} 80 81/* Standard "oh well" can't communicate error. Someday this might attempt 82 synchronization. */ 83static void 84attempt_resync (LPCWSTR huh, int s) 85{ 86 stub_error (L"lost synchronization with host attempting %s. Error %d", huh, WSAGetLastError ()); 87} 88 89/* Read arbitrary stuff from a socket. */ 90static int 91sockread (LPCWSTR huh, int s, void *str, size_t n) 92{ 93 for (;;) 94 { 95 if (recv (s, str, n, 0) == (int) n) 96 return n; 97 attempt_resync (huh, s); 98 } 99} 100 101/* Write arbitrary stuff to a socket. */ 102static int 103sockwrite (LPCWSTR huh, int s, const void *str, size_t n) 104{ 105 for (;;) 106 { 107 if (send (s, str, n, 0) == (int) n) 108 return n; 109 attempt_resync (huh, s); 110 } 111} 112 113/* Get a an ID (possibly) and a DWORD from the host gdb. 114 Don't bother with the id if the main loop has already 115 read it. */ 116static DWORD 117getdword (LPCWSTR huh, int s, gdb_wince_id what_this) 118{ 119 DWORD n; 120 gdb_wince_id what; 121 122 if (skip_next_id) 123 skip_next_id = 0; 124 else 125 do 126 if (sockread (huh, s, &what, sizeof (what)) != sizeof (what)) 127 stub_error (L"error getting record type from host - %s.", huh); 128 while (what_this != what); 129 130 if (sockread (huh, s, &n, sizeof (n)) != sizeof (n)) 131 stub_error (L"error getting %s from host.", huh); 132 133 return n; 134} 135 136/* Get a an ID (possibly) and a WORD from the host gdb. 137 Don't bother with the id if the main loop has already 138 read it. */ 139static WORD 140getword (LPCWSTR huh, int s, gdb_wince_id what_this) 141{ 142 WORD n; 143 gdb_wince_id what; 144 145 if (skip_next_id) 146 skip_next_id = 0; 147 else 148 do 149 if (sockread (huh, s, &what, sizeof (what)) != sizeof (what)) 150 stub_error (L"error getting record type from host - %s.", huh); 151 while (what_this != what); 152 153 if (sockread (huh, s, &n, sizeof (n)) != sizeof (n)) 154 stub_error (L"error getting %s from host.", huh); 155 156 return n; 157} 158 159/* Handy defines for getting various types of values. */ 160#define gethandle(huh, s, what) (HANDLE) getdword ((huh), (s), (what)) 161#define getpvoid(huh, s, what) (LPVOID) getdword ((huh), (s), (what)) 162#define getlen(huh, s, what) (gdb_wince_len) getword ((huh), (s), (what)) 163 164/* Get an arbitrary block of memory from the gdb host. This comes in 165 two chunks an id/dword representing the length and the stream of memory 166 itself. Returns a pointer, allocated via mempool, to a memory buffer. */ 167static LPWSTR 168getmemory (LPCWSTR huh, int s, gdb_wince_id what, gdb_wince_len *inlen) 169{ 170 LPVOID p; 171 gdb_wince_len dummy; 172 173 if (!inlen) 174 inlen = &dummy; 175 176 *inlen = getlen (huh, s, what); 177 178 p = mempool ((unsigned int) *inlen); /* FIXME: check for error */ 179 180 if ((gdb_wince_len) sockread (huh, s, p, *inlen) != *inlen) 181 stub_error (L"error getting string from host."); 182 183 return p; 184} 185 186/* Output an id/dword to the host */ 187static void 188putdword (LPCWSTR huh, int s, gdb_wince_id what, DWORD n) 189{ 190 if (sockwrite (huh, s, &what, sizeof (what)) != sizeof (what)) 191 stub_error (L"error writing record id for %s to host.", huh); 192 if (sockwrite (huh, s, &n, sizeof (n)) != sizeof (n)) 193 stub_error (L"error writing %s to host.", huh); 194} 195 196/* Output an id/word to the host */ 197static void 198putword (LPCWSTR huh, int s, gdb_wince_id what, WORD n) 199{ 200 if (sockwrite (huh, s, &what, sizeof (what)) != sizeof (what)) 201 stub_error (L"error writing record id for %s to host.", huh); 202 if (sockwrite (huh, s, &n, sizeof (n)) != sizeof (n)) 203 stub_error (L"error writing %s to host.", huh); 204} 205 206/* Convenience define for outputting a "gdb_wince_len" type. */ 207#define putlen(huh, s, what, n) putword ((huh), (s), (what), (gdb_wince_len) (n)) 208 209/* Put an arbitrary block of memory to the gdb host. This comes in 210 two chunks an id/dword representing the length and the stream of memory 211 itself. */ 212static void 213putmemory (LPCWSTR huh, int s, gdb_wince_id what, const void *mem, gdb_wince_len len) 214{ 215 putlen (huh, s, what, len); 216 if (((short) len > 0) && (gdb_wince_len) sockwrite (huh, s, mem, len) != len) 217 stub_error (L"error writing memory to host."); 218} 219 220/* Output the result of an operation to the host. If res != 0, sends a block of 221 memory starting at mem of len bytes. If res == 0, sends -GetLastError () and 222 avoids sending the mem. */ 223static void 224putresult (LPCWSTR huh, gdb_wince_result res, int s, gdb_wince_id what, const void *mem, gdb_wince_len len) 225{ 226 if (!res) 227 len = -(int) GetLastError (); 228 putmemory (huh, s, what, mem, len); 229} 230 231static HANDLE curproc; /* Currently unused, but nice for debugging */ 232 233/* Emulate CreateProcess. Returns &pi if no error. */ 234static void 235create_process (int s) 236{ 237 LPWSTR exec_file = getmemory (L"CreateProcess exec_file", s, GDB_CREATEPROCESS, NULL); 238 LPWSTR args = getmemory (L"CreateProcess args", s, GDB_CREATEPROCESS, NULL); 239 DWORD flags = getdword (L"CreateProcess flags", s, GDB_CREATEPROCESS); 240 PROCESS_INFORMATION pi; 241 gdb_wince_result res; 242 243 res = CreateProcessW (exec_file, 244 args, /* command line */ 245 NULL, /* Security */ 246 NULL, /* thread */ 247 FALSE, /* inherit handles */ 248 flags, /* start flags */ 249 NULL, 250 NULL, /* current directory */ 251 NULL, 252 &pi); 253 putresult (L"CreateProcess", res, s, GDB_CREATEPROCESS, &pi, sizeof (pi)); 254 curproc = pi.hProcess; 255} 256 257/* Emulate TerminateProcess. Returns return value of TerminateProcess if 258 no error. 259 *** NOTE: For some unknown reason, TerminateProcess seems to always return 260 an ACCESS_DENIED (on Windows CE???) error. So, force a TRUE value for now. */ 261static void 262terminate_process (int s) 263{ 264 gdb_wince_result res; 265 HANDLE h = gethandle (L"TerminateProcess handle", s, GDB_TERMINATEPROCESS); 266 267 res = TerminateProcess (h, 0) || 1; /* Doesn't seem to work on SH so default to TRUE */ 268 putresult (L"Terminate process result", res, s, GDB_TERMINATEPROCESS, 269 &res, sizeof (res)); 270} 271 272static int stepped = 0; 273/* Handle single step instruction. FIXME: unneded? */ 274static void 275flag_single_step (int s) 276{ 277 stepped = 1; 278 skip_next_id = 0; 279} 280 281struct skipper 282{ 283 wchar_t *s; 284 int nskip; 285} skippy[] = 286{ 287 {L"Undefined Instruction:", 1}, 288 {L"Data Abort:", 2}, 289 {NULL, 0} 290}; 291 292static int 293skip_message (DEBUG_EVENT *ev) 294{ 295 char s[80]; 296 DWORD nread; 297 struct skipper *skp; 298 int nbytes = ev->u.DebugString.nDebugStringLength; 299 300 if (nbytes > sizeof(s)) 301 nbytes = sizeof(s); 302 303 memset (s, 0, sizeof (s)); 304 if (!ReadProcessMemory (curproc, ev->u.DebugString.lpDebugStringData, 305 s, nbytes, &nread)) 306 return 0; 307 308 for (skp = skippy; skp->s != NULL; skp++) 309 if (wcsncmp ((wchar_t *) s, skp->s, wcslen (skp->s)) == 0) 310 return skp->nskip; 311 312 return 0; 313} 314 315/* Emulate WaitForDebugEvent. Returns the debug event on success. */ 316static void 317wait_for_debug_event (int s) 318{ 319 DWORD ms = getdword (L"WaitForDebugEvent ms", s, GDB_WAITFORDEBUGEVENT); 320 gdb_wince_result res; 321 DEBUG_EVENT ev; 322 static int skip_next = 0; 323 324 for (;;) 325 { 326 res = WaitForDebugEvent (&ev, ms); 327 328 if (ev.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) 329 { 330 if (skip_next) 331 { 332 skip_next--; 333 goto ignore; 334 } 335 if (skip_next = skip_message (&ev)) 336 goto ignore; 337 } 338 339 putresult (L"WaitForDebugEvent event", res, s, GDB_WAITFORDEBUGEVENT, 340 &ev, sizeof (ev)); 341 break; 342 343 ignore: 344 ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); 345 } 346 347 return; 348} 349 350/* Emulate GetThreadContext. Returns CONTEXT structure on success. */ 351static void 352get_thread_context (int s) 353{ 354 CONTEXT c; 355 HANDLE h = gethandle (L"GetThreadContext handle", s, GDB_GETTHREADCONTEXT); 356 gdb_wince_result res; 357 358 memset (&c, 0, sizeof (c)); 359 c.ContextFlags = getdword (L"GetThreadContext flags", s, GDB_GETTHREADCONTEXT); 360 361 res = (gdb_wince_result) GetThreadContext (h, &c); 362 putresult (L"GetThreadContext data", res, s, GDB_GETTHREADCONTEXT, 363 &c, sizeof (c)); 364} 365 366/* Emulate GetThreadContext. Returns success of SetThreadContext. */ 367static void 368set_thread_context (int s) 369{ 370 gdb_wince_result res; 371 HANDLE h = gethandle (L"SetThreadContext handle", s, GDB_SETTHREADCONTEXT); 372 LPCONTEXT pc = (LPCONTEXT) getmemory (L"SetThreadContext context", s, 373 GDB_SETTHREADCONTEXT, NULL); 374 375 res = SetThreadContext (h, pc); 376 putresult (L"SetThreadContext result", res, s, GDB_SETTHREADCONTEXT, 377 &res, sizeof (res)); 378} 379 380/* Emulate ReadProcessMemory. Returns memory read on success. */ 381static void 382read_process_memory (int s) 383{ 384 HANDLE h = gethandle (L"ReadProcessMemory handle", s, GDB_READPROCESSMEMORY); 385 LPVOID p = getpvoid (L"ReadProcessMemory base", s, GDB_READPROCESSMEMORY); 386 gdb_wince_len len = getlen (L"ReadProcessMemory size", s, GDB_READPROCESSMEMORY); 387 LPVOID buf = mempool ((unsigned int) len); 388 DWORD outlen; 389 gdb_wince_result res; 390 391 outlen = 0; 392 res = (gdb_wince_result) ReadProcessMemory (h, p, buf, len, &outlen); 393 putresult (L"ReadProcessMemory data", res, s, GDB_READPROCESSMEMORY, 394 buf, (gdb_wince_len) outlen); 395} 396 397/* Emulate WriteProcessMemory. Returns WriteProcessMemory success. */ 398static void 399write_process_memory (int s) 400{ 401 HANDLE h = gethandle (L"WriteProcessMemory handle", s, GDB_WRITEPROCESSMEMORY); 402 LPVOID p = getpvoid (L"WriteProcessMemory base", s, GDB_WRITEPROCESSMEMORY); 403 gdb_wince_len len; 404 LPVOID buf = getmemory (L"WriteProcessMemory buf", s, GDB_WRITEPROCESSMEMORY, &len); 405 DWORD outlen; 406 gdb_wince_result res; 407 408 outlen = 0; 409 res = WriteProcessMemory (h, p, buf, (DWORD) len, &outlen); 410 putresult (L"WriteProcessMemory data", res, s, GDB_WRITEPROCESSMEMORY, 411 (gdb_wince_len *) & outlen, sizeof (gdb_wince_len)); 412} 413 414/* Return non-zero to gdb host if given thread is alive. */ 415static void 416thread_alive (int s) 417{ 418 HANDLE h = gethandle (L"ThreadAlive handle", s, GDB_THREADALIVE); 419 gdb_wince_result res; 420 421 res = WaitForSingleObject (h, 0) == WAIT_OBJECT_0 ? 1 : 0; 422 putresult (L"WriteProcessMemory data", res, s, GDB_THREADALIVE, 423 &res, sizeof (res)); 424} 425 426/* Emulate SuspendThread. Returns value returned from SuspendThread. */ 427static void 428suspend_thread (int s) 429{ 430 DWORD res; 431 HANDLE h = gethandle (L"SuspendThread handle", s, GDB_SUSPENDTHREAD); 432 res = SuspendThread (h); 433 putdword (L"SuspendThread result", s, GDB_SUSPENDTHREAD, res); 434} 435 436/* Emulate ResumeThread. Returns value returned from ResumeThread. */ 437static void 438resume_thread (int s) 439{ 440 DWORD res; 441 HANDLE h = gethandle (L"ResumeThread handle", s, GDB_RESUMETHREAD); 442 res = ResumeThread (h); 443 putdword (L"ResumeThread result", s, GDB_RESUMETHREAD, res); 444} 445 446/* Emulate ContinueDebugEvent. Returns ContinueDebugEvent success. */ 447static void 448continue_debug_event (int s) 449{ 450 gdb_wince_result res; 451 DWORD pid = getdword (L"ContinueDebugEvent pid", s, GDB_CONTINUEDEBUGEVENT); 452 DWORD tid = getdword (L"ContinueDebugEvent tid", s, GDB_CONTINUEDEBUGEVENT); 453 DWORD status = getdword (L"ContinueDebugEvent status", s, GDB_CONTINUEDEBUGEVENT); 454 res = (gdb_wince_result) ContinueDebugEvent (pid, tid, status); 455 putresult (L"ContinueDebugEvent result", res, s, GDB_CONTINUEDEBUGEVENT, &res, sizeof (res)); 456} 457 458/* Emulate CloseHandle. Returns CloseHandle success. */ 459static void 460close_handle (int s) 461{ 462 gdb_wince_result res; 463 HANDLE h = gethandle (L"CloseHandle handle", s, GDB_CLOSEHANDLE); 464 res = (gdb_wince_result) CloseHandle (h); 465 putresult (L"CloseHandle result", res, s, GDB_CLOSEHANDLE, &res, sizeof (res)); 466} 467 468/* Main loop for reading requests from gdb host on the socket. */ 469static void 470dispatch (int s) 471{ 472 gdb_wince_id id; 473 474 /* Continue reading from socket until receive a GDB_STOPSUB. */ 475 while (sockread (L"Dispatch", s, &id, sizeof (id)) > 0) 476 { 477 skip_next_id = 1; 478 switch (id) 479 { 480 case GDB_CREATEPROCESS: 481 create_process (s); 482 break; 483 case GDB_TERMINATEPROCESS: 484 terminate_process (s); 485 break; 486 case GDB_WAITFORDEBUGEVENT: 487 wait_for_debug_event (s); 488 break; 489 case GDB_GETTHREADCONTEXT: 490 get_thread_context (s); 491 break; 492 case GDB_SETTHREADCONTEXT: 493 set_thread_context (s); 494 break; 495 case GDB_READPROCESSMEMORY: 496 read_process_memory (s); 497 break; 498 case GDB_WRITEPROCESSMEMORY: 499 write_process_memory (s); 500 break; 501 case GDB_THREADALIVE: 502 thread_alive (s); 503 break; 504 case GDB_SUSPENDTHREAD: 505 suspend_thread (s); 506 break; 507 case GDB_RESUMETHREAD: 508 resume_thread (s); 509 break; 510 case GDB_CONTINUEDEBUGEVENT: 511 continue_debug_event (s); 512 break; 513 case GDB_CLOSEHANDLE: 514 close_handle (s); 515 break; 516 case GDB_STOPSTUB: 517 terminate_process (s); 518 return; 519 case GDB_SINGLESTEP: 520 flag_single_step (s); 521 break; 522 default: 523 { 524 WCHAR buf[80]; 525 wsprintfW (buf, L"Invalid command id received: %d", id); 526 MessageBoxW (NULL, buf, L"GDB", MB_ICONERROR); 527 skip_next_id = 0; 528 } 529 } 530 } 531} 532 533/* The Windows Main entry point */ 534int WINAPI 535WinMain (HINSTANCE hi, HINSTANCE hp, LPWSTR cmd, int show) 536{ 537 struct hostent *h; 538 int s; 539 struct WSAData wd; 540 struct sockaddr_in sin; 541 int tmp; 542 LPWSTR whost; 543 char host[80]; 544 545 whost = wcschr (cmd, L' '); /* Look for argument. */ 546 547 /* If no host is specified, just use default */ 548 if (whost) 549 { 550 /* Eat any spaces. */ 551 while (*whost == L' ' || *whost == L'\t') 552 whost++; 553 554 wcstombs (host, whost, 80); /* Convert from UNICODE to ascii */ 555 } 556 557 /* Winsock initialization. */ 558 if (WSAStartup (MAKEWORD (1, 1), &wd)) 559 stub_error (L"Couldn't initialize WINSOCK."); 560 561 /* If whost was specified, first try it. If it was not specified or the 562 host lookup failed, try the Windows CE magic ppp_peer lookup. ppp_peer 563 is supposed to be the Windows host sitting on the other end of the 564 serial cable. */ 565 if (whost && *whost && (h = gethostbyname (host)) != NULL) 566 /* nothing to do */ ; 567 else if ((h = gethostbyname ("ppp_peer")) == NULL) 568 stub_error (L"Couldn't get IP address of host system. Error %d", WSAGetLastError ()); 569 570 /* Get a socket. */ 571 if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) 572 stub_error (L"Couldn't connect to host system. Error %d", WSAGetLastError ()); 573 574 /* Allow rapid reuse of the port. */ 575 tmp = 1; 576 setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)); 577 578 /* Set up the information for connecting to the host gdb process. */ 579 memset (&sin, 0, sizeof (sin)); 580 sin.sin_family = h->h_addrtype; 581 memcpy (&sin.sin_addr, h->h_addr, h->h_length); 582 sin.sin_port = htons (7000); /* FIXME: This should be configurable */ 583 584 /* Connect to host */ 585 if (connect (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) 586 stub_error (L"Couldn't connect to host gdb."); 587 588 /* Read from socket until told to exit. */ 589 dispatch (s); 590 WSACleanup (); 591 return 0; 592} 593