Commit | Line | Data |
---|---|---|
0f07afe1 CF |
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 | ||
61c37cee | 31 | #define MALLOC(n) (void *) LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, (UINT)(n)) |
0f07afe1 | 32 | #define REALLOC(s, n) (void *) LocalReAlloc ((HLOCAL)(s), (UINT)(n), LMEM_MOVEABLE) |
61c37cee | 33 | #define FREE(s) LocalFree ((HLOCAL)(s)) |
0f07afe1 CF |
34 | |
35 | static int skip_next_id = 0; /* Don't read next API code from socket */ | |
36 | ||
61c37cee | 37 | /* v-style interface for handling varying argument list error messages. |
0f07afe1 CF |
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 | ||
61c37cee CF |
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 | ||
0f07afe1 CF |
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 | ||
0f07afe1 CF |
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 | ||
61c37cee | 178 | p = mempool ((unsigned int) *inlen); /* FIXME: check for error */ |
0f07afe1 CF |
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 | ||
61c37cee CF |
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 | ||
0f07afe1 CF |
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; | |
61c37cee CF |
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 | } | |
0f07afe1 | 346 | |
61c37cee | 347 | return; |
0f07afe1 CF |
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)); | |
61c37cee | 359 | c.ContextFlags = getdword (L"GetThreadContext flags", s, GDB_GETTHREADCONTEXT); |
0f07afe1 CF |
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); | |
61c37cee | 387 | LPVOID buf = mempool ((unsigned int) len); |
0f07afe1 CF |
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 | ||
0f07afe1 CF |
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: | |
61c37cee CF |
520 | flag_single_step (s); |
521 | break; | |
0f07afe1 CF |
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. */ | |
0f07afe1 CF |
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 | ||
0f07afe1 CF |
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 | } |