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 35 static 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. */ 40 static void 41 vstub_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. */ 52 static void 53 stub_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. */ 63 static LPVOID 64 mempool (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. */ 83 static void 84 attempt_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. */ 90 static int 91 sockread (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. */ 102 static int 103 sockwrite (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. */ 116 static DWORD 117 getdword (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. */ 139 static WORD 140 getword (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. */ 167 static LPWSTR 168 getmemory (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 */ 187 static void 188 putdword (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 */ 197 static void 198 putword (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. */ 212 static void 213 putmemory (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. */ 223 static void 224 putresult (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 231 static HANDLE curproc; /* Currently unused, but nice for debugging */ 232 233 /* Emulate CreateProcess. Returns &pi if no error. */ 234 static void 235 create_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. */ 261 static void 262 terminate_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 272 static int stepped = 0; 273 /* Handle single step instruction. FIXME: unneded? */ 274 static void 275 flag_single_step (int s) 276 { 277 stepped = 1; 278 skip_next_id = 0; 279 } 280 281 struct 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 292 static int 293 skip_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. */ 316 static void 317 wait_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. */ 351 static void 352 get_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. */ 367 static void 368 set_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. */ 381 static void 382 read_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. */ 398 static void 399 write_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. */ 415 static void 416 thread_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. */ 427 static void 428 suspend_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. */ 437 static void 438 resume_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. */ 447 static void 448 continue_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. */ 459 static void 460 close_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. */ 469 static void 470 dispatch (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 */ 534 int WINAPI 535 WinMain (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