]>
Commit | Line | Data |
---|---|---|
b80864fb | 1 | /* Low level interface to Windows debugging, for gdbserver. |
4a94e368 | 2 | Copyright (C) 2006-2022 Free Software Foundation, Inc. |
b80864fb DJ |
3 | |
4 | Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. | |
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 | |
a9762ec7 | 10 | the Free Software Foundation; either version 3 of the License, or |
b80864fb DJ |
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 even the 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 | |
a9762ec7 | 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
b80864fb DJ |
20 | |
21 | #include "server.h" | |
22 | #include "regcache.h" | |
59a016f0 | 23 | #include "gdb/fileio.h" |
ed50f18f PA |
24 | #include "mem-break.h" |
25 | #include "win32-low.h" | |
623b6bdf | 26 | #include "gdbthread.h" |
799cdc37 | 27 | #include "dll.h" |
533b0600 | 28 | #include "hostio.h" |
b80864fb | 29 | #include <windows.h> |
ed50f18f | 30 | #include <winnt.h> |
b80864fb | 31 | #include <imagehlp.h> |
255e7678 | 32 | #include <tlhelp32.h> |
b80864fb | 33 | #include <psapi.h> |
b80864fb | 34 | #include <process.h> |
268a13a5 TT |
35 | #include "gdbsupport/gdb_tilde_expand.h" |
36 | #include "gdbsupport/common-inferior.h" | |
559e7e50 | 37 | #include "gdbsupport/gdb_wait.h" |
b80864fb | 38 | |
4834dad0 TT |
39 | using namespace windows_nat; |
40 | ||
801eb70f TT |
41 | /* See win32-low.h. */ |
42 | windows_process_info windows_process; | |
0578e87f | 43 | |
b80864fb DJ |
44 | #ifndef USE_WIN32API |
45 | #include <sys/cygwin.h> | |
46 | #endif | |
47 | ||
10357975 PA |
48 | #define OUTMSG(X) do { printf X; fflush (stderr); } while (0) |
49 | ||
50 | #define OUTMSG2(X) \ | |
51 | do \ | |
52 | { \ | |
53 | if (debug_threads) \ | |
54 | { \ | |
55 | printf X; \ | |
56 | fflush (stderr); \ | |
57 | } \ | |
58 | } while (0) | |
ed50f18f PA |
59 | |
60 | #ifndef _T | |
61 | #define _T(x) TEXT (x) | |
62 | #endif | |
63 | ||
64 | #ifndef COUNTOF | |
65 | #define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0])) | |
b80864fb DJ |
66 | #endif |
67 | ||
b80864fb DJ |
68 | int using_threads = 1; |
69 | ||
70 | /* Globals. */ | |
d97903b2 | 71 | static int attaching = 0; |
b80864fb | 72 | |
4210d83e PA |
73 | /* A status that hasn't been reported to the core yet, and so |
74 | win32_wait should return it next, instead of fetching the next | |
75 | debug event off the win32 API. */ | |
76 | static struct target_waitstatus cached_status; | |
77 | ||
4d5d1aaa PA |
78 | /* Non zero if an interrupt request is to be satisfied by suspending |
79 | all threads. */ | |
80 | static int soft_interrupt_requested = 0; | |
81 | ||
82 | /* Non zero if the inferior is stopped in a simulated breakpoint done | |
83 | by suspending all the threads. */ | |
84 | static int faked_breakpoint = 0; | |
85 | ||
6479bf85 HD |
86 | /* True if current_process_handle needs to be closed. */ |
87 | static bool open_process_used = false; | |
88 | ||
3aee8918 | 89 | const struct target_desc *win32_tdesc; |
7d186bc0 HD |
90 | #ifdef __x86_64__ |
91 | const struct target_desc *wow64_win32_tdesc; | |
92 | #endif | |
3aee8918 | 93 | |
7d186bc0 | 94 | #define NUM_REGS (the_low_target.num_regs ()) |
b80864fb | 95 | |
b80864fb DJ |
96 | /* Get the thread ID from the current selected inferior (the current |
97 | thread). */ | |
95954743 | 98 | static ptid_t |
0bfdf32f | 99 | current_thread_ptid (void) |
b80864fb | 100 | { |
80894984 | 101 | return current_ptid; |
95954743 PA |
102 | } |
103 | ||
104 | /* The current debug event from WaitForDebugEvent. */ | |
105 | static ptid_t | |
106 | debug_event_ptid (DEBUG_EVENT *event) | |
107 | { | |
fd79271b | 108 | return ptid_t (event->dwProcessId, event->dwThreadId, 0); |
b80864fb DJ |
109 | } |
110 | ||
9c6c8194 PA |
111 | /* Get the thread context of the thread associated with TH. */ |
112 | ||
113 | static void | |
e56f8ccb | 114 | win32_get_thread_context (windows_thread_info *th) |
9c6c8194 | 115 | { |
7d186bc0 | 116 | #ifdef __x86_64__ |
801eb70f | 117 | if (windows_process.wow64_process) |
7d186bc0 HD |
118 | memset (&th->wow64_context, 0, sizeof (WOW64_CONTEXT)); |
119 | else | |
120 | #endif | |
121 | memset (&th->context, 0, sizeof (CONTEXT)); | |
a2abc7de | 122 | (*the_low_target.get_thread_context) (th); |
9c6c8194 PA |
123 | } |
124 | ||
125 | /* Set the thread context of the thread associated with TH. */ | |
126 | ||
127 | static void | |
e56f8ccb | 128 | win32_set_thread_context (windows_thread_info *th) |
9c6c8194 | 129 | { |
7d186bc0 | 130 | #ifdef __x86_64__ |
801eb70f | 131 | if (windows_process.wow64_process) |
de071872 | 132 | Wow64SetThreadContext (th->h, &th->wow64_context); |
d4717483 | 133 | else |
7d186bc0 | 134 | #endif |
d4717483 | 135 | SetThreadContext (th->h, &th->context); |
9c6c8194 PA |
136 | } |
137 | ||
a2abc7de PA |
138 | /* Set the thread context of the thread associated with TH. */ |
139 | ||
140 | static void | |
e56f8ccb | 141 | win32_prepare_to_resume (windows_thread_info *th) |
b80864fb | 142 | { |
a2abc7de PA |
143 | if (the_low_target.prepare_to_resume != NULL) |
144 | (*the_low_target.prepare_to_resume) (th); | |
145 | } | |
b80864fb | 146 | |
a2abc7de | 147 | /* See win32-low.h. */ |
b80864fb | 148 | |
a2abc7de | 149 | void |
e56f8ccb | 150 | win32_require_context (windows_thread_info *th) |
a2abc7de | 151 | { |
7d186bc0 HD |
152 | DWORD context_flags; |
153 | #ifdef __x86_64__ | |
801eb70f | 154 | if (windows_process.wow64_process) |
7d186bc0 HD |
155 | context_flags = th->wow64_context.ContextFlags; |
156 | else | |
157 | #endif | |
158 | context_flags = th->context.ContextFlags; | |
159 | if (context_flags == 0) | |
b80864fb | 160 | { |
98a03287 | 161 | th->suspend (); |
9c6c8194 | 162 | win32_get_thread_context (th); |
b80864fb | 163 | } |
a2abc7de PA |
164 | } |
165 | ||
28688adf TT |
166 | /* See nat/windows-nat.h. */ |
167 | ||
168 | windows_thread_info * | |
0578e87f TT |
169 | windows_nat::windows_process_info::thread_rec |
170 | (ptid_t ptid, thread_disposition_type disposition) | |
a2abc7de | 171 | { |
8dc7b443 | 172 | thread_info *thread = find_thread_ptid (ptid); |
a2abc7de PA |
173 | if (thread == NULL) |
174 | return NULL; | |
175 | ||
e56f8ccb | 176 | windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); |
28688adf | 177 | if (disposition != DONT_INVALIDATE_CONTEXT) |
a2abc7de | 178 | win32_require_context (th); |
b80864fb DJ |
179 | return th; |
180 | } | |
181 | ||
182 | /* Add a thread to the thread list. */ | |
e56f8ccb | 183 | static windows_thread_info * |
711e434b | 184 | child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb) |
b80864fb | 185 | { |
e56f8ccb | 186 | windows_thread_info *th; |
fd79271b | 187 | ptid_t ptid = ptid_t (pid, tid, 0); |
b80864fb | 188 | |
0578e87f | 189 | if ((th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT))) |
b80864fb DJ |
190 | return th; |
191 | ||
7d186bc0 HD |
192 | CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb; |
193 | #ifdef __x86_64__ | |
194 | /* For WOW64 processes, this is actually the pointer to the 64bit TIB, | |
195 | and the 32bit TIB is exactly 2 pages after it. */ | |
801eb70f | 196 | if (windows_process.wow64_process) |
7d186bc0 HD |
197 | base += 2 * 4096; /* page size = 4096 */ |
198 | #endif | |
199 | th = new windows_thread_info (tid, h, base); | |
b80864fb | 200 | |
95954743 | 201 | add_thread (ptid, th); |
b80864fb | 202 | |
34b34921 PA |
203 | if (the_low_target.thread_added != NULL) |
204 | (*the_low_target.thread_added) (th); | |
b80864fb DJ |
205 | |
206 | return th; | |
207 | } | |
208 | ||
209 | /* Delete a thread from the list of threads. */ | |
210 | static void | |
9c80ecd6 | 211 | delete_thread_info (thread_info *thread) |
b80864fb | 212 | { |
e56f8ccb | 213 | windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); |
b80864fb | 214 | |
c3de4d92 | 215 | remove_thread (thread); |
e9534bd2 | 216 | delete th; |
b80864fb DJ |
217 | } |
218 | ||
219 | /* Delete a thread from the list of threads. */ | |
220 | static void | |
95954743 | 221 | child_delete_thread (DWORD pid, DWORD tid) |
b80864fb | 222 | { |
b80864fb | 223 | /* If the last thread is exiting, just return. */ |
9c80ecd6 | 224 | if (all_threads.size () == 1) |
b80864fb DJ |
225 | return; |
226 | ||
8dc7b443 | 227 | thread_info *thread = find_thread_ptid (ptid_t (pid, tid)); |
b80864fb DJ |
228 | if (thread == NULL) |
229 | return; | |
230 | ||
231 | delete_thread_info (thread); | |
232 | } | |
233 | ||
aa5ca48f DE |
234 | /* These watchpoint related wrapper functions simply pass on the function call |
235 | if the low target has registered a corresponding function. */ | |
236 | ||
a2b2297a TBA |
237 | bool |
238 | win32_process_target::supports_z_point_type (char z_type) | |
802e8e6d | 239 | { |
523d4f80 TT |
240 | return (z_type == Z_PACKET_SW_BP |
241 | || (the_low_target.supports_z_point_type != NULL | |
242 | && the_low_target.supports_z_point_type (z_type))); | |
802e8e6d PA |
243 | } |
244 | ||
7e0bde70 TBA |
245 | int |
246 | win32_process_target::insert_point (enum raw_bkpt_type type, CORE_ADDR addr, | |
247 | int size, raw_breakpoint *bp) | |
aa5ca48f | 248 | { |
523d4f80 TT |
249 | if (type == raw_bkpt_type_sw) |
250 | return insert_memory_breakpoint (bp); | |
251 | else if (the_low_target.insert_point != NULL) | |
802e8e6d | 252 | return the_low_target.insert_point (type, addr, size, bp); |
aa5ca48f DE |
253 | else |
254 | /* Unsupported (see target.h). */ | |
255 | return 1; | |
256 | } | |
257 | ||
7e0bde70 TBA |
258 | int |
259 | win32_process_target::remove_point (enum raw_bkpt_type type, CORE_ADDR addr, | |
260 | int size, raw_breakpoint *bp) | |
aa5ca48f | 261 | { |
523d4f80 TT |
262 | if (type == raw_bkpt_type_sw) |
263 | return remove_memory_breakpoint (bp); | |
264 | else if (the_low_target.remove_point != NULL) | |
802e8e6d | 265 | return the_low_target.remove_point (type, addr, size, bp); |
aa5ca48f DE |
266 | else |
267 | /* Unsupported (see target.h). */ | |
268 | return 1; | |
269 | } | |
270 | ||
6eeb5c55 TBA |
271 | bool |
272 | win32_process_target::stopped_by_watchpoint () | |
aa5ca48f DE |
273 | { |
274 | if (the_low_target.stopped_by_watchpoint != NULL) | |
275 | return the_low_target.stopped_by_watchpoint (); | |
276 | else | |
6eeb5c55 | 277 | return false; |
aa5ca48f DE |
278 | } |
279 | ||
6eeb5c55 TBA |
280 | CORE_ADDR |
281 | win32_process_target::stopped_data_address () | |
aa5ca48f DE |
282 | { |
283 | if (the_low_target.stopped_data_address != NULL) | |
284 | return the_low_target.stopped_data_address (); | |
285 | else | |
286 | return 0; | |
287 | } | |
288 | ||
289 | ||
b80864fb DJ |
290 | /* Transfer memory from/to the debugged process. */ |
291 | static int | |
292 | child_xfer_memory (CORE_ADDR memaddr, char *our, int len, | |
5b6d1e4f | 293 | int write, process_stratum_target *target) |
b80864fb | 294 | { |
cee83bcb PM |
295 | BOOL success; |
296 | SIZE_T done = 0; | |
297 | DWORD lasterror = 0; | |
e8f0053d | 298 | uintptr_t addr = (uintptr_t) memaddr; |
b80864fb DJ |
299 | |
300 | if (write) | |
301 | { | |
0578e87f | 302 | success = WriteProcessMemory (windows_process.handle, (LPVOID) addr, |
cee83bcb PM |
303 | (LPCVOID) our, len, &done); |
304 | if (!success) | |
305 | lasterror = GetLastError (); | |
0578e87f | 306 | FlushInstructionCache (windows_process.handle, (LPCVOID) addr, len); |
b80864fb DJ |
307 | } |
308 | else | |
309 | { | |
0578e87f | 310 | success = ReadProcessMemory (windows_process.handle, (LPCVOID) addr, |
cee83bcb PM |
311 | (LPVOID) our, len, &done); |
312 | if (!success) | |
313 | lasterror = GetLastError (); | |
b80864fb | 314 | } |
cee83bcb PM |
315 | if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0) |
316 | return done; | |
317 | else | |
318 | return success ? done : -1; | |
b80864fb DJ |
319 | } |
320 | ||
ed50f18f | 321 | /* Clear out any old thread list and reinitialize it to a pristine |
b80864fb DJ |
322 | state. */ |
323 | static void | |
324 | child_init_thread_list (void) | |
325 | { | |
f0045347 | 326 | for_each_thread (delete_thread_info); |
b80864fb DJ |
327 | } |
328 | ||
f25b3fc3 JB |
329 | /* Zero during the child initialization phase, and nonzero otherwise. */ |
330 | ||
331 | static int child_initialization_done = 0; | |
332 | ||
b80864fb | 333 | static void |
95954743 | 334 | do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) |
b80864fb | 335 | { |
3aee8918 PA |
336 | struct process_info *proc; |
337 | ||
0578e87f TT |
338 | windows_process.last_sig = GDB_SIGNAL_0; |
339 | windows_process.handle = proch; | |
340 | windows_process.id = pid; | |
341 | windows_process.main_thread_id = 0; | |
5ac588cf PA |
342 | |
343 | soft_interrupt_requested = 0; | |
344 | faked_breakpoint = 0; | |
6479bf85 | 345 | open_process_used = true; |
5ac588cf | 346 | |
0578e87f TT |
347 | memset (&windows_process.current_event, 0, |
348 | sizeof (windows_process.current_event)); | |
b80864fb | 349 | |
7d186bc0 HD |
350 | #ifdef __x86_64__ |
351 | BOOL wow64; | |
352 | if (!IsWow64Process (proch, &wow64)) | |
353 | { | |
354 | DWORD err = GetLastError (); | |
355 | error ("Check if WOW64 process failed (error %d): %s\n", | |
356 | (int) err, strwinerror (err)); | |
357 | } | |
801eb70f | 358 | windows_process.wow64_process = wow64; |
7d186bc0 | 359 | |
801eb70f | 360 | if (windows_process.wow64_process |
de071872 TT |
361 | && (Wow64GetThreadContext == nullptr |
362 | || Wow64SetThreadContext == nullptr)) | |
7d186bc0 HD |
363 | error ("WOW64 debugging is not supported on this system.\n"); |
364 | ||
801eb70f TT |
365 | windows_process.ignore_first_breakpoint |
366 | = !attached && windows_process.wow64_process; | |
7d186bc0 HD |
367 | #endif |
368 | ||
3aee8918 | 369 | proc = add_process (pid, attached); |
7d186bc0 | 370 | #ifdef __x86_64__ |
801eb70f | 371 | if (windows_process.wow64_process) |
7d186bc0 HD |
372 | proc->tdesc = wow64_win32_tdesc; |
373 | else | |
374 | #endif | |
375 | proc->tdesc = win32_tdesc; | |
b80864fb | 376 | child_init_thread_list (); |
f25b3fc3 | 377 | child_initialization_done = 0; |
ed50f18f PA |
378 | |
379 | if (the_low_target.initial_stuff != NULL) | |
380 | (*the_low_target.initial_stuff) (); | |
4210d83e | 381 | |
183be222 | 382 | cached_status.set_ignore (); |
4210d83e PA |
383 | |
384 | /* Flush all currently pending debug events (thread and dll list) up | |
385 | to the initial breakpoint. */ | |
386 | while (1) | |
387 | { | |
388 | struct target_waitstatus status; | |
389 | ||
52405d85 | 390 | the_target->wait (minus_one_ptid, &status, 0); |
4210d83e PA |
391 | |
392 | /* Note win32_wait doesn't return thread events. */ | |
183be222 | 393 | if (status.kind () != TARGET_WAITKIND_LOADED) |
4210d83e PA |
394 | { |
395 | cached_status = status; | |
396 | break; | |
397 | } | |
398 | ||
399 | { | |
400 | struct thread_resume resume; | |
401 | ||
402 | resume.thread = minus_one_ptid; | |
403 | resume.kind = resume_continue; | |
404 | resume.sig = 0; | |
405 | ||
52405d85 | 406 | the_target->resume (&resume, 1); |
4210d83e PA |
407 | } |
408 | } | |
379a5e2d | 409 | |
f25b3fc3 JB |
410 | /* Now that the inferior has been started and all DLLs have been mapped, |
411 | we can iterate over all DLLs and load them in. | |
412 | ||
413 | We avoid doing it any earlier because, on certain versions of Windows, | |
414 | LOAD_DLL_DEBUG_EVENTs are sometimes not complete. In particular, | |
415 | we have seen on Windows 8.1 that the ntdll.dll load event does not | |
416 | include the DLL name, preventing us from creating an associated SO. | |
417 | A possible explanation is that ntdll.dll might be mapped before | |
418 | the SO info gets created by the Windows system -- ntdll.dll is | |
419 | the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs | |
420 | do not seem to suffer from that problem. | |
421 | ||
422 | Rather than try to work around this sort of issue, it is much | |
423 | simpler to just ignore DLL load/unload events during the startup | |
424 | phase, and then process them all in one batch now. */ | |
0578e87f | 425 | windows_process.add_all_dlls (); |
f25b3fc3 JB |
426 | |
427 | child_initialization_done = 1; | |
b80864fb DJ |
428 | } |
429 | ||
430 | /* Resume all artificially suspended threads if we are continuing | |
431 | execution. */ | |
2bee2b6c SM |
432 | static void |
433 | continue_one_thread (thread_info *thread, int thread_id) | |
b80864fb | 434 | { |
e56f8ccb | 435 | windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); |
b80864fb | 436 | |
a2abc7de | 437 | if (thread_id == -1 || thread_id == th->tid) |
b80864fb | 438 | { |
a2abc7de | 439 | win32_prepare_to_resume (th); |
34b34921 | 440 | |
a2abc7de | 441 | if (th->suspended) |
c436e841 | 442 | { |
7d186bc0 HD |
443 | DWORD *context_flags; |
444 | #ifdef __x86_64__ | |
801eb70f | 445 | if (windows_process.wow64_process) |
7d186bc0 HD |
446 | context_flags = &th->wow64_context.ContextFlags; |
447 | else | |
448 | #endif | |
449 | context_flags = &th->context.ContextFlags; | |
450 | if (*context_flags) | |
a2abc7de PA |
451 | { |
452 | win32_set_thread_context (th); | |
7d186bc0 | 453 | *context_flags = 0; |
a2abc7de PA |
454 | } |
455 | ||
98a03287 | 456 | th->resume (); |
c436e841 | 457 | } |
b80864fb | 458 | } |
b80864fb DJ |
459 | } |
460 | ||
461 | static BOOL | |
462 | child_continue (DWORD continue_status, int thread_id) | |
463 | { | |
0578e87f TT |
464 | windows_process.desired_stop_thread_id = thread_id; |
465 | if (windows_process.matching_pending_stop (debug_threads)) | |
360ad8b3 TT |
466 | return TRUE; |
467 | ||
4d5d1aaa PA |
468 | /* The inferior will only continue after the ContinueDebugEvent |
469 | call. */ | |
2bee2b6c SM |
470 | for_each_thread ([&] (thread_info *thread) |
471 | { | |
472 | continue_one_thread (thread, thread_id); | |
473 | }); | |
4d5d1aaa | 474 | faked_breakpoint = 0; |
b80864fb | 475 | |
e758e19c | 476 | return continue_last_debug_event (continue_status, debug_threads); |
b80864fb DJ |
477 | } |
478 | ||
b80864fb DJ |
479 | /* Fetch register(s) from the current thread context. */ |
480 | static void | |
442ea881 | 481 | child_fetch_inferior_registers (struct regcache *regcache, int r) |
b80864fb DJ |
482 | { |
483 | int regno; | |
0578e87f TT |
484 | windows_thread_info *th |
485 | = windows_process.thread_rec (current_thread_ptid (), | |
486 | INVALIDATE_CONTEXT); | |
4463ce24 | 487 | if (r == -1 || r > NUM_REGS) |
442ea881 | 488 | child_fetch_inferior_registers (regcache, NUM_REGS); |
b80864fb DJ |
489 | else |
490 | for (regno = 0; regno < r; regno++) | |
442ea881 | 491 | (*the_low_target.fetch_inferior_register) (regcache, th, regno); |
b80864fb DJ |
492 | } |
493 | ||
494 | /* Store a new register value into the current thread context. We don't | |
495 | change the program's context until later, when we resume it. */ | |
496 | static void | |
442ea881 | 497 | child_store_inferior_registers (struct regcache *regcache, int r) |
b80864fb DJ |
498 | { |
499 | int regno; | |
0578e87f TT |
500 | windows_thread_info *th |
501 | = windows_process.thread_rec (current_thread_ptid (), | |
502 | INVALIDATE_CONTEXT); | |
b80864fb | 503 | if (r == -1 || r == 0 || r > NUM_REGS) |
442ea881 | 504 | child_store_inferior_registers (regcache, NUM_REGS); |
b80864fb DJ |
505 | else |
506 | for (regno = 0; regno < r; regno++) | |
442ea881 | 507 | (*the_low_target.store_inferior_register) (regcache, th, regno); |
b80864fb DJ |
508 | } |
509 | ||
ed50f18f PA |
510 | /* Map the Windows error number in ERROR to a locale-dependent error |
511 | message string and return a pointer to it. Typically, the values | |
512 | for ERROR come from GetLastError. | |
513 | ||
514 | The string pointed to shall not be modified by the application, | |
515 | but may be overwritten by a subsequent call to strwinerror | |
516 | ||
517 | The strwinerror function does not change the current setting | |
518 | of GetLastError. */ | |
519 | ||
520 | char * | |
521 | strwinerror (DWORD error) | |
522 | { | |
523 | static char buf[1024]; | |
524 | TCHAR *msgbuf; | |
525 | DWORD lasterr = GetLastError (); | |
526 | DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | |
527 | | FORMAT_MESSAGE_ALLOCATE_BUFFER, | |
528 | NULL, | |
529 | error, | |
530 | 0, /* Default language */ | |
c3de4d92 | 531 | (LPTSTR) &msgbuf, |
ed50f18f PA |
532 | 0, |
533 | NULL); | |
534 | if (chars != 0) | |
535 | { | |
536 | /* If there is an \r\n appended, zap it. */ | |
537 | if (chars >= 2 | |
538 | && msgbuf[chars - 2] == '\r' | |
539 | && msgbuf[chars - 1] == '\n') | |
540 | { | |
541 | chars -= 2; | |
542 | msgbuf[chars] = 0; | |
543 | } | |
544 | ||
545 | if (chars > ((COUNTOF (buf)) - 1)) | |
546 | { | |
547 | chars = COUNTOF (buf) - 1; | |
548 | msgbuf [chars] = 0; | |
549 | } | |
550 | ||
551 | #ifdef UNICODE | |
552 | wcstombs (buf, msgbuf, chars + 1); | |
553 | #else | |
554 | strncpy (buf, msgbuf, chars + 1); | |
555 | #endif | |
556 | LocalFree (msgbuf); | |
557 | } | |
558 | else | |
dfe07582 | 559 | sprintf (buf, "unknown win32 error (%u)", (unsigned) error); |
ed50f18f PA |
560 | |
561 | SetLastError (lasterr); | |
562 | return buf; | |
563 | } | |
564 | ||
aec18585 PA |
565 | static BOOL |
566 | create_process (const char *program, char *args, | |
567 | DWORD flags, PROCESS_INFORMATION *pi) | |
568 | { | |
0b73bf7f | 569 | const std::string &inferior_cwd = get_inferior_cwd (); |
aec18585 | 570 | BOOL ret; |
a9b34532 EZ |
571 | size_t argslen, proglen; |
572 | ||
573 | proglen = strlen (program) + 1; | |
574 | argslen = strlen (args) + proglen; | |
aec18585 | 575 | |
aec18585 | 576 | STARTUPINFOA si = { sizeof (STARTUPINFOA) }; |
a9b34532 EZ |
577 | char *program_and_args = (char *) alloca (argslen + 1); |
578 | ||
579 | strcpy (program_and_args, program); | |
580 | strcat (program_and_args, " "); | |
581 | strcat (program_and_args, args); | |
582 | ret = CreateProcessA (program, /* image name */ | |
583 | program_and_args, /* command line */ | |
584 | NULL, /* security */ | |
585 | NULL, /* thread */ | |
586 | TRUE, /* inherit handles */ | |
587 | flags, /* start flags */ | |
588 | NULL, /* environment */ | |
906994d9 | 589 | /* current directory */ |
0b73bf7f | 590 | (inferior_cwd.empty () |
906994d9 | 591 | ? NULL |
0b73bf7f | 592 | : gdb_tilde_expand (inferior_cwd.c_str ()).c_str()), |
a9b34532 EZ |
593 | &si, /* start info */ |
594 | pi); /* proc info */ | |
aec18585 PA |
595 | |
596 | return ret; | |
597 | } | |
598 | ||
b80864fb | 599 | /* Start a new process. |
2090129c SDJ |
600 | PROGRAM is the program name. |
601 | PROGRAM_ARGS is the vector containing the inferior's args. | |
b80864fb DJ |
602 | Returns the new PID on success, -1 on failure. Registers the new |
603 | process with the process list. */ | |
15295543 TBA |
604 | int |
605 | win32_process_target::create_inferior (const char *program, | |
606 | const std::vector<char *> &program_args) | |
b80864fb | 607 | { |
6341380d | 608 | client_state &cs = get_client_state (); |
b80864fb | 609 | #ifndef USE_WIN32API |
d8d2a3ee | 610 | char real_path[PATH_MAX]; |
b80864fb DJ |
611 | char *orig_path, *new_path, *path_ptr; |
612 | #endif | |
b80864fb DJ |
613 | BOOL ret; |
614 | DWORD flags; | |
ed50f18f | 615 | PROCESS_INFORMATION pi; |
aec18585 | 616 | DWORD err; |
bea571eb | 617 | std::string str_program_args = construct_inferior_arguments (program_args); |
2090129c | 618 | char *args = (char *) str_program_args.c_str (); |
b80864fb | 619 | |
d97903b2 PA |
620 | /* win32_wait needs to know we're not attaching. */ |
621 | attaching = 0; | |
622 | ||
b80864fb DJ |
623 | if (!program) |
624 | error ("No executable specified, specify executable to debug.\n"); | |
625 | ||
b80864fb DJ |
626 | flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; |
627 | ||
628 | #ifndef USE_WIN32API | |
629 | orig_path = NULL; | |
630 | path_ptr = getenv ("PATH"); | |
631 | if (path_ptr) | |
632 | { | |
81239425 | 633 | int size = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, NULL, 0); |
0ae534d2 JT |
634 | orig_path = (char *) alloca (strlen (path_ptr) + 1); |
635 | new_path = (char *) alloca (size); | |
b80864fb | 636 | strcpy (orig_path, path_ptr); |
81239425 | 637 | cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, new_path, size); |
b80864fb | 638 | setenv ("PATH", new_path, 1); |
81239425 | 639 | } |
d8d2a3ee | 640 | cygwin_conv_path (CCP_POSIX_TO_WIN_A, program, real_path, PATH_MAX); |
b80864fb DJ |
641 | program = real_path; |
642 | #endif | |
643 | ||
a9b34532 | 644 | OUTMSG2 (("Command line is \"%s %s\"\n", program, args)); |
b80864fb | 645 | |
ed50f18f | 646 | #ifdef CREATE_NEW_PROCESS_GROUP |
b80864fb | 647 | flags |= CREATE_NEW_PROCESS_GROUP; |
ed50f18f | 648 | #endif |
b80864fb | 649 | |
aec18585 PA |
650 | ret = create_process (program, args, flags, &pi); |
651 | err = GetLastError (); | |
652 | if (!ret && err == ERROR_FILE_NOT_FOUND) | |
653 | { | |
c3de4d92 | 654 | char *exename = (char *) alloca (strlen (program) + 5); |
aec18585 PA |
655 | strcat (strcpy (exename, program), ".exe"); |
656 | ret = create_process (exename, args, flags, &pi); | |
657 | err = GetLastError (); | |
658 | } | |
b80864fb DJ |
659 | |
660 | #ifndef USE_WIN32API | |
661 | if (orig_path) | |
662 | setenv ("PATH", orig_path, 1); | |
663 | #endif | |
664 | ||
665 | if (!ret) | |
666 | { | |
a9b34532 | 667 | error ("Error creating process \"%s %s\", (error %d): %s\n", |
ed50f18f | 668 | program, args, (int) err, strwinerror (err)); |
b80864fb DJ |
669 | } |
670 | else | |
671 | { | |
a9b34532 | 672 | OUTMSG2 (("Process created: %s %s\n", program, (char *) args)); |
b80864fb DJ |
673 | } |
674 | ||
675 | CloseHandle (pi.hThread); | |
676 | ||
95954743 | 677 | do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0); |
b80864fb | 678 | |
7dbac825 JB |
679 | /* Wait till we are at 1st instruction in program, return new pid |
680 | (assuming success). */ | |
0578e87f | 681 | cs.last_ptid = wait (ptid_t (windows_process.id), &cs.last_status, 0); |
7dbac825 | 682 | |
052793ad | 683 | /* Necessary for handle_v_kill. */ |
0578e87f | 684 | signal_pid = windows_process.id; |
052793ad | 685 | |
0578e87f | 686 | return windows_process.id; |
b80864fb DJ |
687 | } |
688 | ||
689 | /* Attach to a running process. | |
690 | PID is the process ID to attach to, specified by the user | |
691 | or a higher layer. */ | |
ef03dad8 TBA |
692 | int |
693 | win32_process_target::attach (unsigned long pid) | |
b80864fb | 694 | { |
5ca906e6 | 695 | HANDLE h; |
5ca906e6 | 696 | DWORD err; |
b80864fb | 697 | |
5ca906e6 PA |
698 | h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); |
699 | if (h != NULL) | |
1d5315fe | 700 | { |
5ca906e6 PA |
701 | if (DebugActiveProcess (pid)) |
702 | { | |
de071872 | 703 | DebugSetProcessKillOnExit (FALSE); |
5ca906e6 | 704 | |
d97903b2 | 705 | /* win32_wait needs to know we're attaching. */ |
1b3f6016 | 706 | attaching = 1; |
95954743 | 707 | do_initial_child_stuff (h, pid, 1); |
5ca906e6 PA |
708 | return 0; |
709 | } | |
710 | ||
711 | CloseHandle (h); | |
b80864fb DJ |
712 | } |
713 | ||
5ca906e6 PA |
714 | err = GetLastError (); |
715 | error ("Attach to process failed (error %d): %s\n", | |
716 | (int) err, strwinerror (err)); | |
b80864fb DJ |
717 | } |
718 | ||
d41b524f TT |
719 | /* See nat/windows-nat.h. */ |
720 | ||
721 | int | |
0578e87f TT |
722 | windows_nat::windows_process_info::handle_output_debug_string |
723 | (struct target_waitstatus *ourstatus) | |
bce7165d PA |
724 | { |
725 | #define READ_BUFFER_LEN 1024 | |
726 | CORE_ADDR addr; | |
727 | char s[READ_BUFFER_LEN + 1] = { 0 }; | |
728 | DWORD nbytes = current_event.u.DebugString.nDebugStringLength; | |
729 | ||
730 | if (nbytes == 0) | |
d41b524f | 731 | return 0; |
bce7165d PA |
732 | |
733 | if (nbytes > READ_BUFFER_LEN) | |
734 | nbytes = READ_BUFFER_LEN; | |
735 | ||
736 | addr = (CORE_ADDR) (size_t) current_event.u.DebugString.lpDebugStringData; | |
737 | ||
738 | if (current_event.u.DebugString.fUnicode) | |
739 | { | |
740 | /* The event tells us how many bytes, not chars, even | |
1b3f6016 | 741 | in Unicode. */ |
bce7165d PA |
742 | WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 }; |
743 | if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0) | |
d41b524f | 744 | return 0; |
bce7165d PA |
745 | wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR)); |
746 | } | |
747 | else | |
748 | { | |
749 | if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0) | |
d41b524f | 750 | return 0; |
bce7165d PA |
751 | } |
752 | ||
61012eef | 753 | if (!startswith (s, "cYg")) |
45e2715e PA |
754 | { |
755 | if (!server_waiting) | |
756 | { | |
757 | OUTMSG2(("%s", s)); | |
d41b524f | 758 | return 0; |
45e2715e PA |
759 | } |
760 | ||
761 | monitor_output (s); | |
762 | } | |
bce7165d | 763 | #undef READ_BUFFER_LEN |
d41b524f TT |
764 | |
765 | return 0; | |
bce7165d PA |
766 | } |
767 | ||
5ac588cf PA |
768 | static void |
769 | win32_clear_inferiors (void) | |
770 | { | |
6479bf85 HD |
771 | if (open_process_used) |
772 | { | |
0578e87f | 773 | CloseHandle (windows_process.handle); |
6479bf85 HD |
774 | open_process_used = false; |
775 | } | |
5ac588cf | 776 | |
f0045347 | 777 | for_each_thread (delete_thread_info); |
0578e87f | 778 | windows_process.siginfo_er.ExceptionCode = 0; |
5ac588cf PA |
779 | clear_inferiors (); |
780 | } | |
781 | ||
a780ef4f PA |
782 | /* Implementation of target_ops::kill. */ |
783 | ||
c6885a57 TBA |
784 | int |
785 | win32_process_target::kill (process_info *process) | |
b80864fb | 786 | { |
0578e87f | 787 | TerminateProcess (windows_process.handle, 0); |
b80864fb DJ |
788 | for (;;) |
789 | { | |
790 | if (!child_continue (DBG_CONTINUE, -1)) | |
791 | break; | |
0578e87f | 792 | if (!wait_for_debug_event (&windows_process.current_event, INFINITE)) |
b80864fb | 793 | break; |
0578e87f TT |
794 | if (windows_process.current_event.dwDebugEventCode |
795 | == EXIT_PROCESS_DEBUG_EVENT) | |
b80864fb | 796 | break; |
0578e87f TT |
797 | else if (windows_process.current_event.dwDebugEventCode |
798 | == OUTPUT_DEBUG_STRING_EVENT) | |
799 | windows_process.handle_output_debug_string (nullptr); | |
b80864fb | 800 | } |
ed50f18f | 801 | |
5ac588cf | 802 | win32_clear_inferiors (); |
95954743 | 803 | |
95954743 PA |
804 | remove_process (process); |
805 | return 0; | |
b80864fb DJ |
806 | } |
807 | ||
ef2ddb33 PA |
808 | /* Implementation of target_ops::detach. */ |
809 | ||
9061c9cf TBA |
810 | int |
811 | win32_process_target::detach (process_info *process) | |
b80864fb | 812 | { |
de071872 TT |
813 | struct thread_resume resume; |
814 | resume.thread = minus_one_ptid; | |
815 | resume.kind = resume_continue; | |
816 | resume.sig = 0; | |
817 | this->resume (&resume, 1); | |
444d6139 | 818 | |
0578e87f | 819 | if (!DebugActiveProcessStop (windows_process.id)) |
5ac588cf PA |
820 | return -1; |
821 | ||
444d6139 | 822 | DebugSetProcessKillOnExit (FALSE); |
95954743 | 823 | remove_process (process); |
444d6139 | 824 | |
5ac588cf | 825 | win32_clear_inferiors (); |
444d6139 PA |
826 | return 0; |
827 | } | |
828 | ||
8adb37b9 TBA |
829 | void |
830 | win32_process_target::mourn (struct process_info *process) | |
505106cd PA |
831 | { |
832 | remove_process (process); | |
833 | } | |
834 | ||
ef2ddb33 PA |
835 | /* Implementation of target_ops::join. */ |
836 | ||
95a49a39 TBA |
837 | void |
838 | win32_process_target::join (int pid) | |
444d6139 | 839 | { |
d105de22 | 840 | HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); |
5ac588cf PA |
841 | if (h != NULL) |
842 | { | |
843 | WaitForSingleObject (h, INFINITE); | |
844 | CloseHandle (h); | |
845 | } | |
b80864fb DJ |
846 | } |
847 | ||
13d3d99b TBA |
848 | /* Return true iff the thread with thread ID TID is alive. */ |
849 | bool | |
850 | win32_process_target::thread_alive (ptid_t ptid) | |
b80864fb | 851 | { |
b80864fb DJ |
852 | /* Our thread list is reliable; don't bother to poll target |
853 | threads. */ | |
8dc7b443 | 854 | return find_thread_ptid (ptid) != NULL; |
b80864fb DJ |
855 | } |
856 | ||
857 | /* Resume the inferior process. RESUME_INFO describes how we want | |
858 | to resume. */ | |
0e4d7e35 TBA |
859 | void |
860 | win32_process_target::resume (thread_resume *resume_info, size_t n) | |
b80864fb DJ |
861 | { |
862 | DWORD tid; | |
2ea28649 | 863 | enum gdb_signal sig; |
b80864fb | 864 | int step; |
e56f8ccb | 865 | windows_thread_info *th; |
b80864fb | 866 | DWORD continue_status = DBG_CONTINUE; |
95954743 | 867 | ptid_t ptid; |
b80864fb DJ |
868 | |
869 | /* This handles the very limited set of resume packets that GDB can | |
870 | currently produce. */ | |
871 | ||
d7e15655 | 872 | if (n == 1 && resume_info[0].thread == minus_one_ptid) |
b80864fb | 873 | tid = -1; |
2bd7c093 | 874 | else if (n > 1) |
b80864fb DJ |
875 | tid = -1; |
876 | else | |
877 | /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make | |
878 | the Windows resume code do the right thing for thread switching. */ | |
0578e87f | 879 | tid = windows_process.current_event.dwThreadId; |
b80864fb | 880 | |
d7e15655 | 881 | if (resume_info[0].thread != minus_one_ptid) |
b80864fb | 882 | { |
ce7715e2 | 883 | sig = gdb_signal_from_host (resume_info[0].sig); |
bd99dc85 | 884 | step = resume_info[0].kind == resume_step; |
b80864fb DJ |
885 | } |
886 | else | |
887 | { | |
ce7715e2 | 888 | sig = GDB_SIGNAL_0; |
b80864fb DJ |
889 | step = 0; |
890 | } | |
891 | ||
a493e3e2 | 892 | if (sig != GDB_SIGNAL_0) |
b80864fb | 893 | { |
0578e87f TT |
894 | if (windows_process.current_event.dwDebugEventCode |
895 | != EXCEPTION_DEBUG_EVENT) | |
b80864fb | 896 | { |
ce7715e2 PA |
897 | OUTMSG (("Cannot continue with signal %s here.\n", |
898 | gdb_signal_to_string (sig))); | |
b80864fb | 899 | } |
0578e87f | 900 | else if (sig == windows_process.last_sig) |
b80864fb DJ |
901 | continue_status = DBG_EXCEPTION_NOT_HANDLED; |
902 | else | |
ce7715e2 | 903 | OUTMSG (("Can only continue with received signal %s.\n", |
0578e87f | 904 | gdb_signal_to_string (windows_process.last_sig))); |
b80864fb DJ |
905 | } |
906 | ||
0578e87f | 907 | windows_process.last_sig = GDB_SIGNAL_0; |
b80864fb DJ |
908 | |
909 | /* Get context for the currently selected thread. */ | |
0578e87f TT |
910 | ptid = debug_event_ptid (&windows_process.current_event); |
911 | th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT); | |
b80864fb DJ |
912 | if (th) |
913 | { | |
a2abc7de PA |
914 | win32_prepare_to_resume (th); |
915 | ||
7d186bc0 HD |
916 | DWORD *context_flags; |
917 | #ifdef __x86_64__ | |
801eb70f | 918 | if (windows_process.wow64_process) |
7d186bc0 HD |
919 | context_flags = &th->wow64_context.ContextFlags; |
920 | else | |
921 | #endif | |
922 | context_flags = &th->context.ContextFlags; | |
923 | if (*context_flags) | |
b80864fb | 924 | { |
b80864fb DJ |
925 | /* Move register values from the inferior into the thread |
926 | context structure. */ | |
927 | regcache_invalidate (); | |
928 | ||
929 | if (step) | |
ed50f18f PA |
930 | { |
931 | if (the_low_target.single_step != NULL) | |
932 | (*the_low_target.single_step) (th); | |
933 | else | |
934 | error ("Single stepping is not supported " | |
935 | "in this configuration.\n"); | |
936 | } | |
34b34921 | 937 | |
9c6c8194 | 938 | win32_set_thread_context (th); |
7d186bc0 | 939 | *context_flags = 0; |
b80864fb DJ |
940 | } |
941 | } | |
942 | ||
943 | /* Allow continuing with the same signal that interrupted us. | |
944 | Otherwise complain. */ | |
945 | ||
946 | child_continue (continue_status, tid); | |
947 | } | |
948 | ||
e228ef97 TT |
949 | /* See nat/windows-nat.h. */ |
950 | ||
951 | void | |
0578e87f TT |
952 | windows_nat::windows_process_info::handle_load_dll (const char *name, |
953 | LPVOID base) | |
255e7678 | 954 | { |
e228ef97 TT |
955 | CORE_ADDR load_addr = (CORE_ADDR) (uintptr_t) base; |
956 | ||
255e7678 DJ |
957 | char buf[MAX_PATH + 1]; |
958 | char buf2[MAX_PATH + 1]; | |
959 | ||
255e7678 DJ |
960 | WIN32_FIND_DATAA w32_fd; |
961 | HANDLE h = FindFirstFileA (name, &w32_fd); | |
255e7678 | 962 | |
850a0f76 JB |
963 | /* The symbols in a dll are offset by 0x1000, which is the |
964 | offset from 0 of the first byte in an image - because | |
965 | of the file header and the section alignment. */ | |
966 | load_addr += 0x1000; | |
967 | ||
255e7678 DJ |
968 | if (h == INVALID_HANDLE_VALUE) |
969 | strcpy (buf, name); | |
970 | else | |
971 | { | |
972 | FindClose (h); | |
973 | strcpy (buf, name); | |
255e7678 DJ |
974 | { |
975 | char cwd[MAX_PATH + 1]; | |
976 | char *p; | |
977 | if (GetCurrentDirectoryA (MAX_PATH + 1, cwd)) | |
978 | { | |
979 | p = strrchr (buf, '\\'); | |
980 | if (p) | |
981 | p[1] = '\0'; | |
982 | SetCurrentDirectoryA (buf); | |
983 | GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p); | |
984 | SetCurrentDirectoryA (cwd); | |
985 | } | |
986 | } | |
255e7678 DJ |
987 | } |
988 | ||
cf6e3471 PA |
989 | if (strcasecmp (buf, "ntdll.dll") == 0) |
990 | { | |
991 | GetSystemDirectoryA (buf, sizeof (buf)); | |
992 | strcat (buf, "\\ntdll.dll"); | |
993 | } | |
cf6e3471 | 994 | |
255e7678 | 995 | #ifdef __CYGWIN__ |
81239425 | 996 | cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2)); |
255e7678 DJ |
997 | #else |
998 | strcpy (buf2, buf); | |
999 | #endif | |
1000 | ||
1001 | loaded_dll (buf2, load_addr); | |
1002 | } | |
1003 | ||
a816ba18 | 1004 | /* See nat/windows-nat.h. */ |
f25b3fc3 | 1005 | |
a816ba18 | 1006 | void |
0578e87f | 1007 | windows_nat::windows_process_info::handle_unload_dll () |
255e7678 DJ |
1008 | { |
1009 | CORE_ADDR load_addr = | |
e8f0053d | 1010 | (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll; |
850a0f76 JB |
1011 | |
1012 | /* The symbols in a dll are offset by 0x1000, which is the | |
1013 | offset from 0 of the first byte in an image - because | |
1014 | of the file header and the section alignment. */ | |
255e7678 DJ |
1015 | load_addr += 0x1000; |
1016 | unloaded_dll (NULL, load_addr); | |
1017 | } | |
1018 | ||
34b34921 | 1019 | static void |
9c80ecd6 | 1020 | suspend_one_thread (thread_info *thread) |
4d5d1aaa | 1021 | { |
e56f8ccb | 1022 | windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); |
4d5d1aaa | 1023 | |
98a03287 | 1024 | th->suspend (); |
4d5d1aaa PA |
1025 | } |
1026 | ||
1027 | static void | |
1028 | fake_breakpoint_event (void) | |
b80864fb | 1029 | { |
4d5d1aaa | 1030 | OUTMSG2(("fake_breakpoint_event\n")); |
b80864fb | 1031 | |
4d5d1aaa PA |
1032 | faked_breakpoint = 1; |
1033 | ||
0578e87f TT |
1034 | memset (&windows_process.current_event, 0, |
1035 | sizeof (windows_process.current_event)); | |
1036 | windows_process.current_event.dwThreadId = windows_process.main_thread_id; | |
1037 | windows_process.current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT; | |
1038 | windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode | |
4d5d1aaa PA |
1039 | = EXCEPTION_BREAKPOINT; |
1040 | ||
f0045347 | 1041 | for_each_thread (suspend_one_thread); |
4d5d1aaa PA |
1042 | } |
1043 | ||
8d30e395 TT |
1044 | /* See nat/windows-nat.h. */ |
1045 | ||
a010605f | 1046 | bool |
0578e87f TT |
1047 | windows_nat::windows_process_info::handle_access_violation |
1048 | (const EXCEPTION_RECORD *rec) | |
a010605f TT |
1049 | { |
1050 | return false; | |
1051 | } | |
1052 | ||
523d4f80 TT |
1053 | /* A helper function that will, if needed, set |
1054 | 'stopped_at_software_breakpoint' on the thread and adjust the | |
1055 | PC. */ | |
1056 | ||
1057 | static void | |
1058 | maybe_adjust_pc () | |
1059 | { | |
1060 | struct regcache *regcache = get_thread_regcache (current_thread, 1); | |
1061 | child_fetch_inferior_registers (regcache, -1); | |
1062 | ||
0578e87f TT |
1063 | windows_thread_info *th |
1064 | = windows_process.thread_rec (current_thread_ptid (), | |
1065 | DONT_INVALIDATE_CONTEXT); | |
523d4f80 TT |
1066 | th->stopped_at_software_breakpoint = false; |
1067 | ||
0578e87f TT |
1068 | if (windows_process.current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT |
1069 | && ((windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode | |
7d186bc0 | 1070 | == EXCEPTION_BREAKPOINT) |
0578e87f | 1071 | || (windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode |
7d186bc0 | 1072 | == STATUS_WX86_BREAKPOINT)) |
523d4f80 TT |
1073 | && child_initialization_done) |
1074 | { | |
1075 | th->stopped_at_software_breakpoint = true; | |
1076 | CORE_ADDR pc = regcache_read_pc (regcache); | |
1077 | CORE_ADDR sw_breakpoint_pc = pc - the_low_target.decr_pc_after_break; | |
1078 | regcache_write_pc (regcache, sw_breakpoint_pc); | |
1079 | } | |
1080 | } | |
1081 | ||
4d5d1aaa PA |
1082 | /* Get the next event from the child. */ |
1083 | ||
1084 | static int | |
8d30e395 TT |
1085 | get_child_debug_event (DWORD *continue_status, |
1086 | struct target_waitstatus *ourstatus) | |
4d5d1aaa | 1087 | { |
95954743 PA |
1088 | ptid_t ptid; |
1089 | ||
0578e87f | 1090 | windows_process.last_sig = GDB_SIGNAL_0; |
183be222 | 1091 | ourstatus->set_spurious (); |
8d30e395 | 1092 | *continue_status = DBG_CONTINUE; |
b80864fb | 1093 | |
4d5d1aaa PA |
1094 | /* Check if GDB sent us an interrupt request. */ |
1095 | check_remote_input_interrupt_request (); | |
1096 | ||
0578e87f TT |
1097 | DEBUG_EVENT *current_event = &windows_process.current_event; |
1098 | ||
4d5d1aaa PA |
1099 | if (soft_interrupt_requested) |
1100 | { | |
1101 | soft_interrupt_requested = 0; | |
1102 | fake_breakpoint_event (); | |
1103 | goto gotevent; | |
1104 | } | |
1105 | ||
d97903b2 | 1106 | attaching = 0; |
84b300de | 1107 | { |
0578e87f TT |
1108 | gdb::optional<pending_stop> stop |
1109 | = windows_process.fetch_pending_stop (debug_threads); | |
84b300de SM |
1110 | if (stop.has_value ()) |
1111 | { | |
1112 | *ourstatus = stop->status; | |
0578e87f TT |
1113 | windows_process.current_event = stop->event; |
1114 | ptid = debug_event_ptid (&windows_process.current_event); | |
24583e45 | 1115 | switch_to_thread (find_thread_ptid (ptid)); |
84b300de SM |
1116 | return 1; |
1117 | } | |
360ad8b3 | 1118 | |
84b300de SM |
1119 | /* Keep the wait time low enough for comfortable remote |
1120 | interruption, but high enough so gdbserver doesn't become a | |
1121 | bottleneck. */ | |
0578e87f | 1122 | if (!wait_for_debug_event (&windows_process.current_event, 250)) |
84b300de SM |
1123 | { |
1124 | DWORD e = GetLastError(); | |
912cf4ba | 1125 | |
84b300de SM |
1126 | if (e == ERROR_PIPE_NOT_CONNECTED) |
1127 | { | |
1128 | /* This will happen if the loader fails to succesfully | |
1129 | load the application, e.g., if the main executable | |
1130 | tries to pull in a non-existing export from a | |
1131 | DLL. */ | |
183be222 | 1132 | ourstatus->set_exited (1); |
84b300de SM |
1133 | return 1; |
1134 | } | |
912cf4ba | 1135 | |
84b300de SM |
1136 | return 0; |
1137 | } | |
1138 | } | |
4d5d1aaa PA |
1139 | |
1140 | gotevent: | |
b80864fb | 1141 | |
0578e87f | 1142 | switch (current_event->dwDebugEventCode) |
b80864fb DJ |
1143 | { |
1144 | case CREATE_THREAD_DEBUG_EVENT: | |
1145 | OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " | |
dfe07582 | 1146 | "for pid=%u tid=%x)\n", |
0578e87f TT |
1147 | (unsigned) current_event->dwProcessId, |
1148 | (unsigned) current_event->dwThreadId)); | |
b80864fb DJ |
1149 | |
1150 | /* Record the existence of this thread. */ | |
0578e87f TT |
1151 | child_add_thread (current_event->dwProcessId, |
1152 | current_event->dwThreadId, | |
1153 | current_event->u.CreateThread.hThread, | |
1154 | current_event->u.CreateThread.lpThreadLocalBase); | |
b80864fb DJ |
1155 | break; |
1156 | ||
1157 | case EXIT_THREAD_DEBUG_EVENT: | |
1158 | OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " | |
dfe07582 | 1159 | "for pid=%u tid=%x\n", |
0578e87f TT |
1160 | (unsigned) current_event->dwProcessId, |
1161 | (unsigned) current_event->dwThreadId)); | |
1162 | child_delete_thread (current_event->dwProcessId, | |
1163 | current_event->dwThreadId); | |
aeeb81d1 | 1164 | |
24583e45 | 1165 | switch_to_thread (get_first_thread ()); |
aeeb81d1 | 1166 | return 1; |
b80864fb DJ |
1167 | |
1168 | case CREATE_PROCESS_DEBUG_EVENT: | |
1169 | OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " | |
dfe07582 | 1170 | "for pid=%u tid=%x\n", |
0578e87f TT |
1171 | (unsigned) current_event->dwProcessId, |
1172 | (unsigned) current_event->dwThreadId)); | |
1173 | CloseHandle (current_event->u.CreateProcessInfo.hFile); | |
b80864fb | 1174 | |
6479bf85 HD |
1175 | if (open_process_used) |
1176 | { | |
0578e87f | 1177 | CloseHandle (windows_process.handle); |
6479bf85 HD |
1178 | open_process_used = false; |
1179 | } | |
1180 | ||
0578e87f TT |
1181 | windows_process.handle = current_event->u.CreateProcessInfo.hProcess; |
1182 | windows_process.main_thread_id = current_event->dwThreadId; | |
b80864fb | 1183 | |
b80864fb | 1184 | /* Add the main thread. */ |
0578e87f TT |
1185 | child_add_thread (current_event->dwProcessId, |
1186 | windows_process.main_thread_id, | |
1187 | current_event->u.CreateProcessInfo.hThread, | |
1188 | current_event->u.CreateProcessInfo.lpThreadLocalBase); | |
b80864fb DJ |
1189 | break; |
1190 | ||
1191 | case EXIT_PROCESS_DEBUG_EVENT: | |
1192 | OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " | |
dfe07582 | 1193 | "for pid=%u tid=%x\n", |
0578e87f TT |
1194 | (unsigned) current_event->dwProcessId, |
1195 | (unsigned) current_event->dwThreadId)); | |
559e7e50 | 1196 | { |
0578e87f | 1197 | DWORD exit_status = current_event->u.ExitProcess.dwExitCode; |
559e7e50 EZ |
1198 | /* If the exit status looks like a fatal exception, but we |
1199 | don't recognize the exception's code, make the original | |
1200 | exit status value available, to avoid losing information. */ | |
1201 | int exit_signal | |
1202 | = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1; | |
1203 | if (exit_signal == -1) | |
183be222 | 1204 | ourstatus->set_exited (exit_status); |
559e7e50 | 1205 | else |
183be222 | 1206 | ourstatus->set_signalled (gdb_signal_from_host (exit_signal)); |
559e7e50 | 1207 | } |
0578e87f | 1208 | child_continue (DBG_CONTINUE, windows_process.desired_stop_thread_id); |
b80864fb DJ |
1209 | break; |
1210 | ||
1211 | case LOAD_DLL_DEBUG_EVENT: | |
1212 | OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT " | |
dfe07582 | 1213 | "for pid=%u tid=%x\n", |
0578e87f TT |
1214 | (unsigned) current_event->dwProcessId, |
1215 | (unsigned) current_event->dwThreadId)); | |
1216 | CloseHandle (current_event->u.LoadDll.hFile); | |
f25b3fc3 JB |
1217 | if (! child_initialization_done) |
1218 | break; | |
0578e87f | 1219 | windows_process.dll_loaded_event (); |
b80864fb | 1220 | |
183be222 | 1221 | ourstatus->set_loaded (); |
b80864fb DJ |
1222 | break; |
1223 | ||
1224 | case UNLOAD_DLL_DEBUG_EVENT: | |
1225 | OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " | |
dfe07582 | 1226 | "for pid=%u tid=%x\n", |
0578e87f TT |
1227 | (unsigned) current_event->dwProcessId, |
1228 | (unsigned) current_event->dwThreadId)); | |
f25b3fc3 JB |
1229 | if (! child_initialization_done) |
1230 | break; | |
0578e87f | 1231 | windows_process.handle_unload_dll (); |
183be222 | 1232 | ourstatus->set_loaded (); |
b80864fb DJ |
1233 | break; |
1234 | ||
1235 | case EXCEPTION_DEBUG_EVENT: | |
1236 | OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " | |
dfe07582 | 1237 | "for pid=%u tid=%x\n", |
0578e87f TT |
1238 | (unsigned) current_event->dwProcessId, |
1239 | (unsigned) current_event->dwThreadId)); | |
1240 | if (windows_process.handle_exception (ourstatus, debug_threads) | |
8d30e395 TT |
1241 | == HANDLE_EXCEPTION_UNHANDLED) |
1242 | *continue_status = DBG_EXCEPTION_NOT_HANDLED; | |
b80864fb DJ |
1243 | break; |
1244 | ||
1245 | case OUTPUT_DEBUG_STRING_EVENT: | |
1246 | /* A message from the kernel (or Cygwin). */ | |
1247 | OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT " | |
dfe07582 | 1248 | "for pid=%u tid=%x\n", |
0578e87f TT |
1249 | (unsigned) current_event->dwProcessId, |
1250 | (unsigned) current_event->dwThreadId)); | |
1251 | windows_process.handle_output_debug_string (nullptr); | |
b80864fb DJ |
1252 | break; |
1253 | ||
1254 | default: | |
1255 | OUTMSG2 (("gdbserver: kernel event unknown " | |
dfe07582 | 1256 | "for pid=%u tid=%x code=%x\n", |
0578e87f TT |
1257 | (unsigned) current_event->dwProcessId, |
1258 | (unsigned) current_event->dwThreadId, | |
1259 | (unsigned) current_event->dwDebugEventCode)); | |
b80864fb DJ |
1260 | break; |
1261 | } | |
1262 | ||
0578e87f | 1263 | ptid = debug_event_ptid (&windows_process.current_event); |
360ad8b3 | 1264 | |
0578e87f TT |
1265 | if (windows_process.desired_stop_thread_id != -1 |
1266 | && windows_process.desired_stop_thread_id != ptid.lwp ()) | |
360ad8b3 TT |
1267 | { |
1268 | /* Pending stop. See the comment by the definition of | |
1269 | "pending_stops" for details on why this is needed. */ | |
1270 | OUTMSG2 (("get_windows_debug_event - " | |
e2275c6e | 1271 | "unexpected stop in 0x%lx (expecting 0x%x)\n", |
0578e87f | 1272 | ptid.lwp (), windows_process.desired_stop_thread_id)); |
360ad8b3 | 1273 | maybe_adjust_pc (); |
0578e87f TT |
1274 | windows_process.pending_stops.push_back |
1275 | ({(DWORD) ptid.lwp (), *ourstatus, *current_event}); | |
183be222 | 1276 | ourstatus->set_spurious (); |
360ad8b3 TT |
1277 | } |
1278 | else | |
24583e45 | 1279 | switch_to_thread (find_thread_ptid (ptid)); |
360ad8b3 | 1280 | |
4d5d1aaa | 1281 | return 1; |
b80864fb DJ |
1282 | } |
1283 | ||
1284 | /* Wait for the inferior process to change state. | |
1285 | STATUS will be filled in with a response code to send to GDB. | |
1286 | Returns the signal which caused the process to stop. */ | |
6532e7e3 TBA |
1287 | ptid_t |
1288 | win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus, | |
b60cea74 | 1289 | target_wait_flags options) |
b80864fb | 1290 | { |
183be222 | 1291 | if (cached_status.kind () != TARGET_WAITKIND_IGNORE) |
4210d83e PA |
1292 | { |
1293 | /* The core always does a wait after creating the inferior, and | |
1294 | do_initial_child_stuff already ran the inferior to the | |
1295 | initial breakpoint (or an exit, if creating the process | |
1296 | fails). Report it now. */ | |
1297 | *ourstatus = cached_status; | |
183be222 | 1298 | cached_status.set_ignore (); |
0578e87f | 1299 | return debug_event_ptid (&windows_process.current_event); |
4210d83e PA |
1300 | } |
1301 | ||
b80864fb DJ |
1302 | while (1) |
1303 | { | |
8d30e395 TT |
1304 | DWORD continue_status; |
1305 | if (!get_child_debug_event (&continue_status, ourstatus)) | |
4d5d1aaa | 1306 | continue; |
b80864fb | 1307 | |
183be222 | 1308 | switch (ourstatus->kind ()) |
b80864fb | 1309 | { |
34b34921 | 1310 | case TARGET_WAITKIND_EXITED: |
b80864fb | 1311 | OUTMSG2 (("Child exited with retcode = %x\n", |
183be222 | 1312 | ourstatus->exit_status ())); |
5ac588cf | 1313 | win32_clear_inferiors (); |
0578e87f | 1314 | return ptid_t (windows_process.current_event.dwProcessId); |
34b34921 | 1315 | case TARGET_WAITKIND_STOPPED: |
559e7e50 | 1316 | case TARGET_WAITKIND_SIGNALLED: |
1b3f6016 | 1317 | case TARGET_WAITKIND_LOADED: |
523d4f80 TT |
1318 | { |
1319 | OUTMSG2 (("Child Stopped with signal = %d \n", | |
183be222 | 1320 | ourstatus->sig ())); |
523d4f80 | 1321 | maybe_adjust_pc (); |
0578e87f | 1322 | return debug_event_ptid (&windows_process.current_event); |
523d4f80 | 1323 | } |
1b3f6016 | 1324 | default: |
183be222 SM |
1325 | OUTMSG (("Ignoring unknown internal event, %d\n", |
1326 | ourstatus->kind ())); | |
1b3f6016 PA |
1327 | /* fall-through */ |
1328 | case TARGET_WAITKIND_SPURIOUS: | |
34b34921 | 1329 | /* do nothing, just continue */ |
0578e87f TT |
1330 | child_continue (continue_status, |
1331 | windows_process.desired_stop_thread_id); | |
34b34921 | 1332 | break; |
b80864fb | 1333 | } |
b80864fb DJ |
1334 | } |
1335 | } | |
1336 | ||
1337 | /* Fetch registers from the inferior process. | |
1338 | If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */ | |
a5a4d4cd TBA |
1339 | void |
1340 | win32_process_target::fetch_registers (regcache *regcache, int regno) | |
b80864fb | 1341 | { |
442ea881 | 1342 | child_fetch_inferior_registers (regcache, regno); |
b80864fb DJ |
1343 | } |
1344 | ||
1345 | /* Store registers to the inferior process. | |
1346 | If REGNO is -1, store all registers; otherwise, store at least REGNO. */ | |
a5a4d4cd TBA |
1347 | void |
1348 | win32_process_target::store_registers (regcache *regcache, int regno) | |
b80864fb | 1349 | { |
442ea881 | 1350 | child_store_inferior_registers (regcache, regno); |
b80864fb DJ |
1351 | } |
1352 | ||
1353 | /* Read memory from the inferior process. This should generally be | |
1354 | called through read_inferior_memory, which handles breakpoint shadowing. | |
1355 | Read LEN bytes at MEMADDR into a buffer at MYADDR. */ | |
e2558df3 TBA |
1356 | int |
1357 | win32_process_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr, | |
1358 | int len) | |
b80864fb | 1359 | { |
ed50f18f | 1360 | return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len; |
b80864fb DJ |
1361 | } |
1362 | ||
1363 | /* Write memory to the inferior process. This should generally be | |
1364 | called through write_inferior_memory, which handles breakpoint shadowing. | |
1365 | Write LEN bytes from the buffer at MYADDR to MEMADDR. | |
1366 | Returns 0 on success and errno on failure. */ | |
e2558df3 TBA |
1367 | int |
1368 | win32_process_target::write_memory (CORE_ADDR memaddr, | |
1369 | const unsigned char *myaddr, int len) | |
b80864fb DJ |
1370 | { |
1371 | return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; | |
1372 | } | |
1373 | ||
7390519e | 1374 | /* Send an interrupt request to the inferior process. */ |
eb497a2a TBA |
1375 | void |
1376 | win32_process_target::request_interrupt () | |
7390519e | 1377 | { |
0578e87f | 1378 | if (GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, windows_process.id)) |
7390519e PA |
1379 | return; |
1380 | ||
1381 | /* GenerateConsoleCtrlEvent can fail if process id being debugged is | |
1382 | not a process group id. | |
1383 | Fallback to XP/Vista 'DebugBreakProcess', which generates a | |
1384 | breakpoint exception in the interior process. */ | |
1385 | ||
0578e87f | 1386 | if (DebugBreakProcess (windows_process.handle)) |
7390519e PA |
1387 | return; |
1388 | ||
4d5d1aaa PA |
1389 | /* Last resort, suspend all threads manually. */ |
1390 | soft_interrupt_requested = 1; | |
7390519e PA |
1391 | } |
1392 | ||
22aa6223 TBA |
1393 | bool |
1394 | win32_process_target::supports_hardware_single_step () | |
1395 | { | |
1396 | return true; | |
1397 | } | |
1398 | ||
d7abedf7 TBA |
1399 | bool |
1400 | win32_process_target::supports_qxfer_siginfo () | |
1401 | { | |
1402 | return true; | |
1403 | } | |
1404 | ||
7928d571 HD |
1405 | /* Write Windows signal info. */ |
1406 | ||
d7abedf7 TBA |
1407 | int |
1408 | win32_process_target::qxfer_siginfo (const char *annex, | |
1409 | unsigned char *readbuf, | |
1410 | unsigned const char *writebuf, | |
1411 | CORE_ADDR offset, int len) | |
7928d571 | 1412 | { |
0578e87f | 1413 | if (windows_process.siginfo_er.ExceptionCode == 0) |
7928d571 HD |
1414 | return -1; |
1415 | ||
1416 | if (readbuf == nullptr) | |
1417 | return -1; | |
1418 | ||
0578e87f TT |
1419 | char *buf = (char *) &windows_process.siginfo_er; |
1420 | size_t bufsize = sizeof (windows_process.siginfo_er); | |
7d186bc0 HD |
1421 | |
1422 | #ifdef __x86_64__ | |
1423 | EXCEPTION_RECORD32 er32; | |
801eb70f | 1424 | if (windows_process.wow64_process) |
7d186bc0 HD |
1425 | { |
1426 | buf = (char *) &er32; | |
1427 | bufsize = sizeof (er32); | |
1428 | ||
801eb70f TT |
1429 | er32.ExceptionCode = windows_process.siginfo_er.ExceptionCode; |
1430 | er32.ExceptionFlags = windows_process.siginfo_er.ExceptionFlags; | |
1431 | er32.ExceptionRecord | |
1432 | = (uintptr_t) windows_process.siginfo_er.ExceptionRecord; | |
1433 | er32.ExceptionAddress | |
1434 | = (uintptr_t) windows_process.siginfo_er.ExceptionAddress; | |
1435 | er32.NumberParameters = windows_process.siginfo_er.NumberParameters; | |
7d186bc0 HD |
1436 | int i; |
1437 | for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++) | |
801eb70f TT |
1438 | er32.ExceptionInformation[i] |
1439 | = windows_process.siginfo_er.ExceptionInformation[i]; | |
7d186bc0 HD |
1440 | } |
1441 | #endif | |
1442 | ||
1443 | if (offset > bufsize) | |
7928d571 HD |
1444 | return -1; |
1445 | ||
7d186bc0 HD |
1446 | if (offset + len > bufsize) |
1447 | len = bufsize - offset; | |
7928d571 | 1448 | |
7d186bc0 | 1449 | memcpy (readbuf, buf + offset, len); |
7928d571 HD |
1450 | |
1451 | return len; | |
1452 | } | |
1453 | ||
4e2e869c TBA |
1454 | bool |
1455 | win32_process_target::supports_get_tib_address () | |
1456 | { | |
1457 | return true; | |
1458 | } | |
1459 | ||
711e434b PM |
1460 | /* Write Windows OS Thread Information Block address. */ |
1461 | ||
4e2e869c TBA |
1462 | int |
1463 | win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr) | |
711e434b | 1464 | { |
e56f8ccb | 1465 | windows_thread_info *th; |
0578e87f | 1466 | th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT); |
711e434b PM |
1467 | if (th == NULL) |
1468 | return 0; | |
1469 | if (addr != NULL) | |
1470 | *addr = th->thread_local_base; | |
1471 | return 1; | |
1472 | } | |
1473 | ||
fb78e89c AT |
1474 | /* Implementation of the target_ops method "sw_breakpoint_from_kind". */ |
1475 | ||
d367006f TBA |
1476 | const gdb_byte * |
1477 | win32_process_target::sw_breakpoint_from_kind (int kind, int *size) | |
fb78e89c AT |
1478 | { |
1479 | *size = the_low_target.breakpoint_len; | |
1480 | return the_low_target.breakpoint; | |
1481 | } | |
1482 | ||
523d4f80 TT |
1483 | bool |
1484 | win32_process_target::stopped_by_sw_breakpoint () | |
1485 | { | |
0578e87f TT |
1486 | windows_thread_info *th |
1487 | = windows_process.thread_rec (current_thread_ptid (), | |
1488 | DONT_INVALIDATE_CONTEXT); | |
523d4f80 TT |
1489 | return th == nullptr ? false : th->stopped_at_software_breakpoint; |
1490 | } | |
1491 | ||
1492 | bool | |
1493 | win32_process_target::supports_stopped_by_sw_breakpoint () | |
1494 | { | |
1495 | return true; | |
1496 | } | |
1497 | ||
d6225aff TT |
1498 | CORE_ADDR |
1499 | win32_process_target::read_pc (struct regcache *regcache) | |
1500 | { | |
1501 | return (*the_low_target.get_pc) (regcache); | |
1502 | } | |
1503 | ||
1504 | void | |
1505 | win32_process_target::write_pc (struct regcache *regcache, CORE_ADDR pc) | |
1506 | { | |
1507 | return (*the_low_target.set_pc) (regcache, pc); | |
1508 | } | |
1509 | ||
42a59714 TT |
1510 | const char * |
1511 | win32_process_target::thread_name (ptid_t thread) | |
1512 | { | |
1513 | windows_thread_info *th | |
1514 | = windows_process.thread_rec (current_thread_ptid (), | |
1515 | DONT_INVALIDATE_CONTEXT); | |
8bbdbd69 | 1516 | return th->thread_name (); |
42a59714 TT |
1517 | } |
1518 | ||
5ef9273d TBA |
1519 | /* The win32 target ops object. */ |
1520 | ||
1521 | static win32_process_target the_win32_target; | |
1522 | ||
b80864fb DJ |
1523 | /* Initialize the Win32 backend. */ |
1524 | void | |
1525 | initialize_low (void) | |
1526 | { | |
52405d85 | 1527 | set_target_ops (&the_win32_target); |
d05b4ac3 | 1528 | the_low_target.arch_setup (); |
7d186bc0 | 1529 | |
de071872 | 1530 | initialize_loadable (); |
b80864fb | 1531 | } |