forked from Imagelibrary/binutils-gdb
Compare commits
35 Commits
gdb-15.1-r
...
users/palv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41330f5d32 | ||
|
|
5223cde0d5 | ||
|
|
7d6c04c580 | ||
|
|
650daf1818 | ||
|
|
6b25d11b01 | ||
|
|
af7fc7ff9e | ||
|
|
acd3803fa9 | ||
|
|
13c13b58d2 | ||
|
|
7ad98390f7 | ||
|
|
467b267632 | ||
|
|
a0bc4291f1 | ||
|
|
41f9f2d609 | ||
|
|
0c6b048236 | ||
|
|
f5e5342583 | ||
|
|
f428cac615 | ||
|
|
11adfeba32 | ||
|
|
5ad78cc624 | ||
|
|
02ff02e05f | ||
|
|
4ae1d3c664 | ||
|
|
3a84bb42a3 | ||
|
|
3f6f23ee4e | ||
|
|
734404845d | ||
|
|
8055ea0bb6 | ||
|
|
49fcf1ccc7 | ||
|
|
44fff407b7 | ||
|
|
552616cbb7 | ||
|
|
87e727c0c6 | ||
|
|
f99b2890dd | ||
|
|
b67852992b | ||
|
|
c151e663fa | ||
|
|
78ef7c48d1 | ||
|
|
ea80c32fa7 | ||
|
|
446babcd70 | ||
|
|
cefdca26f9 | ||
|
|
b93d04b777 |
3
gdb/NEWS
3
gdb/NEWS
@@ -9,6 +9,9 @@
|
||||
* Building GDB and GDBserver now requires a C++17 compiler.
|
||||
For example, GCC 9 or later.
|
||||
|
||||
* The Windows native target now supports non-stop mode. This feature
|
||||
requires Windows 10 or later.
|
||||
|
||||
* GDB index now contains information about the main function. This speeds up
|
||||
startup when it is being used for some large binaries.
|
||||
|
||||
|
||||
36
gdb/infrun.c
36
gdb/infrun.c
@@ -365,6 +365,42 @@ update_signals_program_target (void)
|
||||
target_program_signals (signal_program);
|
||||
}
|
||||
|
||||
/* See infrun.h. */
|
||||
|
||||
gdb_signal
|
||||
get_detach_signal (process_stratum_target *proc_target, ptid_t ptid)
|
||||
{
|
||||
thread_info *tp = proc_target->find_thread (ptid);
|
||||
gdb_signal signo = GDB_SIGNAL_0;
|
||||
|
||||
if (target_is_non_stop_p () && !tp->executing ())
|
||||
{
|
||||
if (tp->has_pending_waitstatus ())
|
||||
{
|
||||
/* If the thread has a pending event, and it was stopped
|
||||
with a signal, use that signal to resume it. If it has a
|
||||
pending event of another kind, it was not stopped with a
|
||||
signal, so resume it without a signal. */
|
||||
if (tp->pending_waitstatus ().kind () == TARGET_WAITKIND_STOPPED)
|
||||
signo = tp->pending_waitstatus ().sig ();
|
||||
}
|
||||
else
|
||||
signo = tp->stop_signal ();
|
||||
}
|
||||
else if (!target_is_non_stop_p ())
|
||||
{
|
||||
ptid_t last_ptid;
|
||||
process_stratum_target *last_target;
|
||||
|
||||
get_last_target_status (&last_target, &last_ptid, nullptr);
|
||||
|
||||
if (last_target == proc_target && ptid == last_ptid)
|
||||
signo = tp->stop_signal ();
|
||||
}
|
||||
|
||||
return signo;
|
||||
}
|
||||
|
||||
/* Value to pass to target_resume() to cause all threads to resume. */
|
||||
|
||||
#define RESUME_ALL minus_one_ptid
|
||||
|
||||
@@ -319,6 +319,12 @@ extern void all_uis_on_sync_execution_starting (void);
|
||||
detach. */
|
||||
extern void restart_after_all_stop_detach (process_stratum_target *proc_target);
|
||||
|
||||
/* While detaching, return the signal PTID was supposed to be resumed
|
||||
with, if it were resumed, so we can pass it down to PTID while
|
||||
detaching. */
|
||||
extern gdb_signal get_detach_signal (process_stratum_target *proc_target,
|
||||
ptid_t ptid);
|
||||
|
||||
/* RAII object to temporarily disable the requirement for target
|
||||
stacks to commit their resumed threads.
|
||||
|
||||
|
||||
@@ -1286,13 +1286,13 @@ detach_one_pid (int pid, int signo)
|
||||
pid, strsignal (signo));
|
||||
}
|
||||
|
||||
/* Get pending signal of THREAD as a host signal number, for detaching
|
||||
/* Get pending signal of LP as a host signal number, for detaching
|
||||
purposes. This is the signal the thread last stopped for, which we
|
||||
need to deliver to the thread when detaching, otherwise, it'd be
|
||||
suppressed/lost. */
|
||||
|
||||
static int
|
||||
get_detach_signal (struct lwp_info *lp)
|
||||
get_lwp_detach_signal (struct lwp_info *lp)
|
||||
{
|
||||
enum gdb_signal signo = GDB_SIGNAL_0;
|
||||
|
||||
@@ -1322,37 +1322,7 @@ get_detach_signal (struct lwp_info *lp)
|
||||
else if (lp->status)
|
||||
signo = gdb_signal_from_host (WSTOPSIG (lp->status));
|
||||
else
|
||||
{
|
||||
thread_info *tp = linux_target->find_thread (lp->ptid);
|
||||
|
||||
if (target_is_non_stop_p () && !tp->executing ())
|
||||
{
|
||||
if (tp->has_pending_waitstatus ())
|
||||
{
|
||||
/* If the thread has a pending event, and it was stopped with a
|
||||
signal, use that signal to resume it. If it has a pending
|
||||
event of another kind, it was not stopped with a signal, so
|
||||
resume it without a signal. */
|
||||
if (tp->pending_waitstatus ().kind () == TARGET_WAITKIND_STOPPED)
|
||||
signo = tp->pending_waitstatus ().sig ();
|
||||
else
|
||||
signo = GDB_SIGNAL_0;
|
||||
}
|
||||
else
|
||||
signo = tp->stop_signal ();
|
||||
}
|
||||
else if (!target_is_non_stop_p ())
|
||||
{
|
||||
ptid_t last_ptid;
|
||||
process_stratum_target *last_target;
|
||||
|
||||
get_last_target_status (&last_target, &last_ptid, nullptr);
|
||||
|
||||
if (last_target == linux_target
|
||||
&& lp->ptid.lwp () == last_ptid.lwp ())
|
||||
signo = tp->stop_signal ();
|
||||
}
|
||||
}
|
||||
signo = get_detach_signal (linux_target, lp->ptid);
|
||||
|
||||
if (signo == GDB_SIGNAL_0)
|
||||
{
|
||||
|
||||
@@ -153,6 +153,55 @@ windows_thread_info::thread_name ()
|
||||
return name.get ();
|
||||
}
|
||||
|
||||
/* Read Windows signal info. See nat/windows-nat.h. */
|
||||
|
||||
bool
|
||||
windows_thread_info::xfer_siginfo (gdb_byte *readbuf,
|
||||
ULONGEST offset, ULONGEST len,
|
||||
ULONGEST *xfered_len)
|
||||
{
|
||||
if (last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
|
||||
return false;
|
||||
|
||||
if (readbuf == nullptr)
|
||||
return false;
|
||||
|
||||
EXCEPTION_RECORD &er = last_event.u.Exception.ExceptionRecord;
|
||||
|
||||
char *buf = (char *) &er;
|
||||
size_t bufsize = sizeof (er);
|
||||
|
||||
#ifdef __x86_64__
|
||||
EXCEPTION_RECORD32 er32;
|
||||
if (proc->wow64_process)
|
||||
{
|
||||
buf = (char *) &er32;
|
||||
bufsize = sizeof (er32);
|
||||
|
||||
er32.ExceptionCode = er.ExceptionCode;
|
||||
er32.ExceptionFlags = er.ExceptionFlags;
|
||||
er32.ExceptionRecord
|
||||
= (uintptr_t) er.ExceptionRecord;
|
||||
er32.ExceptionAddress
|
||||
= (uintptr_t) er.ExceptionAddress;
|
||||
er32.NumberParameters = er.NumberParameters;
|
||||
for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
|
||||
er32.ExceptionInformation[i] = er.ExceptionInformation[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (offset > bufsize)
|
||||
return false;
|
||||
|
||||
if (offset + len > bufsize)
|
||||
len = bufsize - offset;
|
||||
|
||||
memcpy (readbuf, buf + offset, len);
|
||||
*xfered_len = len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to determine the executable filename.
|
||||
|
||||
EXE_NAME_RET is a pointer to a buffer whose size is EXE_NAME_MAX_LEN.
|
||||
@@ -310,8 +359,10 @@ get_image_name (HANDLE h, void *address, int unicode)
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
bool
|
||||
windows_process_info::handle_ms_vc_exception (const EXCEPTION_RECORD *rec)
|
||||
windows_process_info::handle_ms_vc_exception (const DEBUG_EVENT ¤t_event)
|
||||
{
|
||||
const EXCEPTION_RECORD *rec = ¤t_event.u.Exception.ExceptionRecord;
|
||||
|
||||
if (rec->NumberParameters >= 3
|
||||
&& (rec->ExceptionInformation[0] & 0xffffffff) == 0x1000)
|
||||
{
|
||||
@@ -325,9 +376,8 @@ windows_process_info::handle_ms_vc_exception (const EXCEPTION_RECORD *rec)
|
||||
if (named_thread_id == (DWORD) -1)
|
||||
named_thread_id = current_event.dwThreadId;
|
||||
|
||||
named_thread = thread_rec (ptid_t (current_event.dwProcessId,
|
||||
named_thread_id, 0),
|
||||
DONT_INVALIDATE_CONTEXT);
|
||||
named_thread = find_thread (ptid_t (current_event.dwProcessId,
|
||||
named_thread_id, 0));
|
||||
if (named_thread != NULL)
|
||||
{
|
||||
int thread_name_len;
|
||||
@@ -353,7 +403,8 @@ windows_process_info::handle_ms_vc_exception (const EXCEPTION_RECORD *rec)
|
||||
#define MS_VC_EXCEPTION 0x406d1388
|
||||
|
||||
handle_exception_result
|
||||
windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
|
||||
windows_process_info::handle_exception (DEBUG_EVENT ¤t_event,
|
||||
struct target_waitstatus *ourstatus,
|
||||
bool debug_exceptions)
|
||||
{
|
||||
#define DEBUG_EXCEPTION_SIMPLE(x) if (debug_exceptions) \
|
||||
@@ -365,14 +416,6 @@ windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
|
||||
DWORD code = rec->ExceptionCode;
|
||||
handle_exception_result result = HANDLE_EXCEPTION_HANDLED;
|
||||
|
||||
memcpy (&siginfo_er, rec, sizeof siginfo_er);
|
||||
|
||||
/* Record the context of the current thread. */
|
||||
thread_rec (ptid_t (current_event.dwProcessId, current_event.dwThreadId, 0),
|
||||
DONT_SUSPEND);
|
||||
|
||||
last_sig = GDB_SIGNAL_0;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
@@ -485,7 +528,7 @@ windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
|
||||
break;
|
||||
case MS_VC_EXCEPTION:
|
||||
DEBUG_EXCEPTION_SIMPLE ("MS_VC_EXCEPTION");
|
||||
if (handle_ms_vc_exception (rec))
|
||||
if (handle_ms_vc_exception (current_event))
|
||||
{
|
||||
ourstatus->set_stopped (GDB_SIGNAL_TRAP);
|
||||
result = HANDLE_EXCEPTION_IGNORED;
|
||||
@@ -506,7 +549,11 @@ windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
|
||||
}
|
||||
|
||||
if (ourstatus->kind () == TARGET_WAITKIND_STOPPED)
|
||||
last_sig = ourstatus->sig ();
|
||||
{
|
||||
ptid_t ptid (current_event.dwProcessId, current_event.dwThreadId, 0);
|
||||
windows_thread_info *th = find_thread (ptid);
|
||||
th->last_sig = ourstatus->sig ();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -639,11 +686,11 @@ windows_process_info::add_dll (LPVOID load_addr)
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
void
|
||||
windows_process_info::dll_loaded_event ()
|
||||
windows_process_info::dll_loaded_event (const DEBUG_EVENT ¤t_event)
|
||||
{
|
||||
gdb_assert (current_event.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT);
|
||||
|
||||
LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
|
||||
const LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
|
||||
const char *dll_name;
|
||||
|
||||
/* Try getting the DLL name via the lpImageName field of the event.
|
||||
@@ -671,69 +718,29 @@ windows_process_info::add_all_dlls ()
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
bool
|
||||
windows_process_info::matching_pending_stop (bool debug_events)
|
||||
ptid_t
|
||||
get_last_debug_event_ptid ()
|
||||
{
|
||||
/* If there are pending stops, and we might plausibly hit one of
|
||||
them, we don't want to actually continue the inferior -- we just
|
||||
want to report the stop. In this case, we just pretend to
|
||||
continue. See the comment by the definition of "pending_stops"
|
||||
for details on why this is needed. */
|
||||
for (const auto &item : pending_stops)
|
||||
{
|
||||
if (desired_stop_thread_id == -1
|
||||
|| desired_stop_thread_id == item.thread_id)
|
||||
{
|
||||
DEBUG_EVENTS ("pending stop anticipated, desired=0x%x, item=0x%x",
|
||||
desired_stop_thread_id, item.thread_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
std::optional<pending_stop>
|
||||
windows_process_info::fetch_pending_stop (bool debug_events)
|
||||
{
|
||||
std::optional<pending_stop> result;
|
||||
for (auto iter = pending_stops.begin ();
|
||||
iter != pending_stops.end ();
|
||||
++iter)
|
||||
{
|
||||
if (desired_stop_thread_id == -1
|
||||
|| desired_stop_thread_id == iter->thread_id)
|
||||
{
|
||||
result = *iter;
|
||||
current_event = iter->event;
|
||||
|
||||
DEBUG_EVENTS ("pending stop found in 0x%x (desired=0x%x)",
|
||||
iter->thread_id, desired_stop_thread_id);
|
||||
|
||||
pending_stops.erase (iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return ptid_t (last_wait_event.dwProcessId, last_wait_event.dwThreadId, 0);
|
||||
}
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
BOOL
|
||||
continue_last_debug_event (DWORD continue_status, bool debug_events)
|
||||
continue_last_debug_event (DWORD cont_status, bool debug_events)
|
||||
{
|
||||
DEBUG_EVENTS ("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s)",
|
||||
(unsigned) last_wait_event.dwProcessId,
|
||||
(unsigned) last_wait_event.dwThreadId,
|
||||
continue_status == DBG_CONTINUE ?
|
||||
"DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED");
|
||||
DEBUG_EVENTS
|
||||
("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s)",
|
||||
(unsigned) last_wait_event.dwProcessId,
|
||||
(unsigned) last_wait_event.dwThreadId,
|
||||
cont_status == DBG_CONTINUE ? "DBG_CONTINUE" :
|
||||
cont_status == DBG_EXCEPTION_NOT_HANDLED ? "DBG_EXCEPTION_NOT_HANDLED" :
|
||||
cont_status == DBG_REPLY_LATER ? "DBG_REPLY_LATER" :
|
||||
"DBG_???");
|
||||
|
||||
return ContinueDebugEvent (last_wait_event.dwProcessId,
|
||||
last_wait_event.dwThreadId,
|
||||
continue_status);
|
||||
cont_status);
|
||||
}
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
@@ -937,6 +944,26 @@ disable_randomization_available ()
|
||||
|
||||
/* See windows-nat.h. */
|
||||
|
||||
bool
|
||||
dbg_reply_later_available ()
|
||||
{
|
||||
static int available = -1;
|
||||
if (available == -1)
|
||||
{
|
||||
/* DBG_REPLY_LATER is supported since Windows 10, Version 1507.
|
||||
If supported, this fails with ERROR_INVALID_HANDLE (tested on
|
||||
Win10 and Win11). If not supported, it fails with
|
||||
ERROR_INVALID_PARAMETER (tested on Win7). */
|
||||
if (ContinueDebugEvent (0, 0, DBG_REPLY_LATER))
|
||||
internal_error (_("ContinueDebugEvent call should not "
|
||||
"have succeeded"));
|
||||
available = (GetLastError () != ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
/* See windows-nat.h. */
|
||||
|
||||
bool
|
||||
initialize_loadable ()
|
||||
{
|
||||
|
||||
@@ -32,12 +32,34 @@
|
||||
namespace windows_nat
|
||||
{
|
||||
|
||||
struct windows_process_info;
|
||||
|
||||
/* The reason for explicitly stopping a thread. Note the enumerators
|
||||
are ordered such that when comparing two stopping_kind's numerical
|
||||
value, the highest should prevail. */
|
||||
enum stopping_kind
|
||||
{
|
||||
/* Not really stopping the thread. */
|
||||
SK_NOT_STOPPING = 0,
|
||||
|
||||
/* We're stopping the thread for internal reasons, the stop should
|
||||
not be reported as an event to the core. */
|
||||
SK_INTERNAL = 1,
|
||||
|
||||
/* We're stopping the thread for external reasons, meaning, the
|
||||
core/user asked us to stop the thread, so we must report a stop
|
||||
event to the core. */
|
||||
SK_EXTERNAL = 2,
|
||||
};
|
||||
|
||||
/* Thread information structure used to track extra information about
|
||||
each thread. */
|
||||
struct windows_thread_info
|
||||
{
|
||||
windows_thread_info (DWORD tid_, HANDLE h_, CORE_ADDR tlb)
|
||||
: tid (tid_),
|
||||
windows_thread_info (windows_process_info *proc_,
|
||||
DWORD tid_, HANDLE h_, CORE_ADDR tlb)
|
||||
: proc (proc_),
|
||||
tid (tid_),
|
||||
h (h_),
|
||||
thread_local_base (tlb)
|
||||
{
|
||||
@@ -56,6 +78,18 @@ struct windows_thread_info
|
||||
the next call. */
|
||||
const char *thread_name ();
|
||||
|
||||
/* Read LEN bytes of the thread's $_siginfo into READBUF, starting
|
||||
at OFFSET. Store the number of actually-read bytes in
|
||||
XFERED_LEN. Returns true on success, false on failure. Passing
|
||||
READBUF as NULL indicates that the caller is trying to write to
|
||||
$_siginfo, which is a failure case. */
|
||||
bool xfer_siginfo (gdb_byte *readbuf,
|
||||
ULONGEST offset, ULONGEST len,
|
||||
ULONGEST *xfered_len);
|
||||
|
||||
/* The process this thread belongs to. */
|
||||
windows_process_info *proc;
|
||||
|
||||
/* The Win32 thread identifier. */
|
||||
DWORD tid;
|
||||
|
||||
@@ -65,12 +99,79 @@ struct windows_thread_info
|
||||
/* Thread Information Block address. */
|
||||
CORE_ADDR thread_local_base;
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
/* These two fields are used to handle Cygwin signals. When a
|
||||
thread is signaled, the "sig" thread inside the Cygwin runtime
|
||||
reports the fact to us via a special OutputDebugString message.
|
||||
In order to make stepping into a signal handler work, we can only
|
||||
resume the "sig" thread when we also resume the target signaled
|
||||
thread. When we intercept a Cygwin signal, we set up a cross
|
||||
link between the two threads using the two fields below, so we
|
||||
can always identify one from the other. See the "Cygwin signals"
|
||||
description in gdb/windows-nat.c for more. */
|
||||
|
||||
/* If this thread received a signal, then 'cygwin_sig_thread' points
|
||||
to the "sig" thread within the Cygwin runtime. */
|
||||
windows_thread_info *cygwin_sig_thread = nullptr;
|
||||
|
||||
/* If this thread is the Cygwin runtime's "sig" thread, then
|
||||
'signaled_thread' points at the thread that received a
|
||||
signal. */
|
||||
windows_thread_info *signaled_thread = nullptr;
|
||||
#endif
|
||||
|
||||
/* If the thread had its event postponed with DBG_REPLY_LATER, when
|
||||
we later ResumeThread this thread, WaitForDebugEvent will
|
||||
re-report the postponed event. This field holds the continue
|
||||
status value to be automatically passed to ContinueDebugEvent
|
||||
when we encounter this re-reported event. 0 if the thread has
|
||||
not had its event postponed with DBG_REPLY_LATER. */
|
||||
DWORD reply_later = 0;
|
||||
|
||||
/* This keeps track of whether SuspendThread was called on this
|
||||
thread. -1 means there was a failure or that the thread was
|
||||
explicitly not suspended, 1 means it was called, and 0 means it
|
||||
was not. */
|
||||
int suspended = 0;
|
||||
|
||||
/* This flag indicates whether we are explicitly stopping this
|
||||
thread in response to a target_stop request or for
|
||||
backend-internal reasons. This allows distinguishing between
|
||||
threads that are explicitly stopped by the debugger and threads
|
||||
that are stopped due to other reasons.
|
||||
|
||||
Typically, when we want to stop a thread, we suspend it, enqueue
|
||||
a pending GDB_SIGNAL_0 stop status on the thread, and then set
|
||||
this flag to true. However, if the thread has had its event
|
||||
previously postponed with DBG_REPLY_LATER, it means that it
|
||||
already has an event to report. In such case, we simply set the
|
||||
'stopping' flag without suspending the thread or enqueueing a
|
||||
pending stop. See stop_one_thread. */
|
||||
stopping_kind stopping = SK_NOT_STOPPING;
|
||||
|
||||
/* Info about a potential pending stop.
|
||||
|
||||
Sometimes, Windows will report a stop on a thread that has been
|
||||
ostensibly suspended. We believe what happens here is that two
|
||||
threads hit a breakpoint simultaneously, and the Windows kernel
|
||||
queues the stop events. However, this can result in the strange
|
||||
effect of trying to single step thread A -- leaving all other
|
||||
threads suspended -- and then seeing a stop in thread B. To handle
|
||||
this scenario, we queue all such "pending" stops here, and then
|
||||
process them once the step has completed. See PR gdb/22992.
|
||||
|
||||
TARGET_WAITKIND_IGNORE if the thread does not have a pending
|
||||
stop. */
|
||||
target_waitstatus pending_status;
|
||||
|
||||
/* The last Windows event returned by WaitForDebugEvent for this
|
||||
thread. */
|
||||
DEBUG_EVENT last_event {};
|
||||
|
||||
/* The last signal reported for this thread, extracted out of
|
||||
last_event. */
|
||||
enum gdb_signal last_sig = GDB_SIGNAL_0;
|
||||
|
||||
/* The context of the thread, including any manipulations. */
|
||||
union
|
||||
{
|
||||
@@ -84,10 +185,6 @@ struct windows_thread_info
|
||||
the thread. */
|
||||
bool debug_registers_changed = false;
|
||||
|
||||
/* Nonzero if CONTEXT is invalidated and must be re-read from the
|
||||
inferior thread. */
|
||||
bool reload_context = false;
|
||||
|
||||
/* True if this thread is currently stopped at a software
|
||||
breakpoint. This is used to offset the PC when needed. */
|
||||
bool stopped_at_software_breakpoint = false;
|
||||
@@ -101,34 +198,6 @@ struct windows_thread_info
|
||||
gdb::unique_xmalloc_ptr<char> name;
|
||||
};
|
||||
|
||||
|
||||
/* Possible values to pass to 'thread_rec'. */
|
||||
enum thread_disposition_type
|
||||
{
|
||||
/* Do not invalidate the thread's context, and do not suspend the
|
||||
thread. */
|
||||
DONT_INVALIDATE_CONTEXT,
|
||||
/* Invalidate the context, but do not suspend the thread. */
|
||||
DONT_SUSPEND,
|
||||
/* Invalidate the context and suspend the thread. */
|
||||
INVALIDATE_CONTEXT
|
||||
};
|
||||
|
||||
/* A single pending stop. See "pending_stops" for more
|
||||
information. */
|
||||
struct pending_stop
|
||||
{
|
||||
/* The thread id. */
|
||||
DWORD thread_id;
|
||||
|
||||
/* The target waitstatus we computed. */
|
||||
target_waitstatus status;
|
||||
|
||||
/* The event. A few fields of this can be referenced after a stop,
|
||||
and it seemed simplest to store the entire event. */
|
||||
DEBUG_EVENT event;
|
||||
};
|
||||
|
||||
enum handle_exception_result
|
||||
{
|
||||
HANDLE_EXCEPTION_UNHANDLED = 0,
|
||||
@@ -144,31 +213,8 @@ struct windows_process_info
|
||||
{
|
||||
/* The process handle */
|
||||
HANDLE handle = 0;
|
||||
DWORD process_id = 0;
|
||||
DWORD main_thread_id = 0;
|
||||
enum gdb_signal last_sig = GDB_SIGNAL_0;
|
||||
|
||||
/* The current debug event from WaitForDebugEvent or from a pending
|
||||
stop. */
|
||||
DEBUG_EVENT current_event {};
|
||||
|
||||
/* The ID of the thread for which we anticipate a stop event.
|
||||
Normally this is -1, meaning we'll accept an event in any
|
||||
thread. */
|
||||
DWORD desired_stop_thread_id = -1;
|
||||
|
||||
/* A vector of pending stops. Sometimes, Windows will report a stop
|
||||
on a thread that has been ostensibly suspended. We believe what
|
||||
happens here is that two threads hit a breakpoint simultaneously,
|
||||
and the Windows kernel queues the stop events. However, this can
|
||||
result in the strange effect of trying to single step thread A --
|
||||
leaving all other threads suspended -- and then seeing a stop in
|
||||
thread B. To handle this scenario, we queue all such "pending"
|
||||
stops here, and then process them once the step has completed. See
|
||||
PR gdb/22992. */
|
||||
std::vector<pending_stop> pending_stops;
|
||||
|
||||
/* Contents of $_siginfo */
|
||||
EXCEPTION_RECORD siginfo_er {};
|
||||
|
||||
#ifdef __x86_64__
|
||||
/* The target is a WOW64 process */
|
||||
@@ -177,24 +223,21 @@ struct windows_process_info
|
||||
bool ignore_first_breakpoint = false;
|
||||
#endif
|
||||
|
||||
|
||||
/* Find a thread record given a thread id. THREAD_DISPOSITION
|
||||
controls whether the thread is suspended, and whether the context
|
||||
is invalidated.
|
||||
/* Find a thread record given a thread id.
|
||||
|
||||
This function must be supplied by the embedding application. */
|
||||
virtual windows_thread_info *thread_rec (ptid_t ptid,
|
||||
thread_disposition_type disposition) = 0;
|
||||
virtual windows_thread_info *find_thread (ptid_t ptid) = 0;
|
||||
|
||||
/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. Updates
|
||||
OURSTATUS and returns the thread id if this represents a thread
|
||||
change (this is specific to Cygwin), otherwise 0.
|
||||
OURSTATUS and returns true if this represents a Cygwin signal,
|
||||
otherwise false.
|
||||
|
||||
Cygwin prepends its messages with a "cygwin:". Interpret this as
|
||||
a Cygwin signal. Otherwise just print the string as a warning.
|
||||
|
||||
This function must be supplied by the embedding application. */
|
||||
virtual int handle_output_debug_string (struct target_waitstatus *ourstatus) = 0;
|
||||
virtual bool handle_output_debug_string (const DEBUG_EVENT ¤t_event,
|
||||
struct target_waitstatus *ourstatus) = 0;
|
||||
|
||||
/* Handle a DLL load event.
|
||||
|
||||
@@ -215,7 +258,7 @@ struct windows_process_info
|
||||
|
||||
This function must be supplied by the embedding application. */
|
||||
|
||||
virtual void handle_unload_dll () = 0;
|
||||
virtual void handle_unload_dll (const DEBUG_EVENT ¤t_event) = 0;
|
||||
|
||||
/* When EXCEPTION_ACCESS_VIOLATION is processed, we give the embedding
|
||||
application a chance to change it to be considered "unhandled".
|
||||
@@ -224,30 +267,26 @@ struct windows_process_info
|
||||
|
||||
virtual bool handle_access_violation (const EXCEPTION_RECORD *rec) = 0;
|
||||
|
||||
/* Fill in the thread's CONTEXT/WOW64_CONTEXT, if it wasn't filled
|
||||
in yet.
|
||||
|
||||
This function must be supplied by the embedding application. */
|
||||
|
||||
virtual void fill_thread_context (windows_thread_info *th) = 0;
|
||||
|
||||
handle_exception_result handle_exception
|
||||
(struct target_waitstatus *ourstatus, bool debug_exceptions);
|
||||
(DEBUG_EVENT ¤t_event,
|
||||
struct target_waitstatus *ourstatus, bool debug_exceptions);
|
||||
|
||||
/* Call to indicate that a DLL was loaded. */
|
||||
|
||||
void dll_loaded_event ();
|
||||
void dll_loaded_event (const DEBUG_EVENT ¤t_event);
|
||||
|
||||
/* Iterate over all DLLs currently mapped by our inferior, and
|
||||
add them to our list of solibs. */
|
||||
|
||||
void add_all_dlls ();
|
||||
|
||||
/* Return true if there is a pending stop matching
|
||||
desired_stop_thread_id. If DEBUG_EVENTS is true, logging will be
|
||||
enabled. */
|
||||
|
||||
bool matching_pending_stop (bool debug_events);
|
||||
|
||||
/* See if a pending stop matches DESIRED_STOP_THREAD_ID. If so,
|
||||
remove it from the list of pending stops, set 'current_event', and
|
||||
return it. Otherwise, return an empty optional. */
|
||||
|
||||
std::optional<pending_stop> fetch_pending_stop (bool debug_events);
|
||||
|
||||
const char *pid_to_exec_file (int);
|
||||
|
||||
private:
|
||||
@@ -258,7 +297,7 @@ private:
|
||||
|
||||
Return true if the exception was handled; return false otherwise. */
|
||||
|
||||
bool handle_ms_vc_exception (const EXCEPTION_RECORD *rec);
|
||||
bool handle_ms_vc_exception (const DEBUG_EVENT ¤t_event);
|
||||
|
||||
/* Iterate over all DLLs currently mapped by our inferior, looking for
|
||||
a DLL which is loaded at LOAD_ADDR. If found, add the DLL to our
|
||||
@@ -289,6 +328,11 @@ private:
|
||||
extern BOOL continue_last_debug_event (DWORD continue_status,
|
||||
bool debug_events);
|
||||
|
||||
/* Return the ptid_t of the thread that the last waited-for event was
|
||||
for. */
|
||||
|
||||
extern ptid_t get_last_debug_event_ptid ();
|
||||
|
||||
/* A simple wrapper for WaitForDebugEvent that also sets the internal
|
||||
'last_wait_event' on success. */
|
||||
|
||||
@@ -433,6 +477,15 @@ extern DeleteProcThreadAttributeList_ftype *DeleteProcThreadAttributeList;
|
||||
|
||||
extern bool disable_randomization_available ();
|
||||
|
||||
/* This is available starting with Windows 10. */
|
||||
#ifndef DBG_REPLY_LATER
|
||||
# define DBG_REPLY_LATER 0x40010001L
|
||||
#endif
|
||||
|
||||
/* Return true if it's possible to use DBG_REPLY_LATER with
|
||||
ContinueDebugEvent on this host. */
|
||||
extern bool dbg_reply_later_available ();
|
||||
|
||||
/* Load any functions which may not be available in ancient versions
|
||||
of Windows. */
|
||||
|
||||
|
||||
1799
gdb/windows-nat.c
1799
gdb/windows-nat.c
File diff suppressed because it is too large
Load Diff
@@ -141,18 +141,21 @@ win32_require_context (windows_thread_info *th)
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
void
|
||||
gdbserver_windows_process::fill_thread_context (windows_thread_info *th)
|
||||
{
|
||||
win32_require_context (th);
|
||||
}
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
windows_thread_info *
|
||||
gdbserver_windows_process::thread_rec
|
||||
(ptid_t ptid, thread_disposition_type disposition)
|
||||
gdbserver_windows_process::find_thread (ptid_t ptid)
|
||||
{
|
||||
thread_info *thread = find_thread_ptid (ptid);
|
||||
if (thread == NULL)
|
||||
return NULL;
|
||||
|
||||
windows_thread_info *th = (windows_thread_info *) thread_target_data (thread);
|
||||
if (disposition != DONT_INVALIDATE_CONTEXT)
|
||||
win32_require_context (th);
|
||||
return th;
|
||||
if (thread == nullptr)
|
||||
return nullptr;
|
||||
return (windows_thread_info *) thread_target_data (thread);
|
||||
}
|
||||
|
||||
/* Add a thread to the thread list. */
|
||||
@@ -162,7 +165,7 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
|
||||
windows_thread_info *th;
|
||||
ptid_t ptid = ptid_t (pid, tid, 0);
|
||||
|
||||
if ((th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT)))
|
||||
if ((th = windows_process.find_thread (ptid)))
|
||||
return th;
|
||||
|
||||
CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb;
|
||||
@@ -172,7 +175,7 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
|
||||
if (windows_process.wow64_process)
|
||||
base += 2 * 4096; /* page size = 4096 */
|
||||
#endif
|
||||
th = new windows_thread_info (tid, h, base);
|
||||
th = new windows_thread_info (&windows_process, tid, h, base);
|
||||
|
||||
add_thread (ptid, th);
|
||||
|
||||
@@ -307,17 +310,12 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
|
||||
{
|
||||
struct process_info *proc;
|
||||
|
||||
windows_process.last_sig = GDB_SIGNAL_0;
|
||||
windows_process.handle = proch;
|
||||
windows_process.process_id = pid;
|
||||
windows_process.main_thread_id = 0;
|
||||
|
||||
windows_process.soft_interrupt_requested = 0;
|
||||
windows_process.faked_breakpoint = 0;
|
||||
windows_process.open_process_used = true;
|
||||
|
||||
memset (&windows_process.current_event, 0,
|
||||
sizeof (windows_process.current_event));
|
||||
|
||||
#ifdef __x86_64__
|
||||
BOOL wow64;
|
||||
if (!IsWow64Process (proch, &wow64))
|
||||
@@ -424,24 +422,20 @@ continue_one_thread (thread_info *thread, int thread_id)
|
||||
}
|
||||
|
||||
th->resume ();
|
||||
th->last_sig = GDB_SIGNAL_0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL
|
||||
child_continue (DWORD continue_status, int thread_id)
|
||||
child_continue_for_kill (DWORD continue_status, int thread_id)
|
||||
{
|
||||
windows_process.desired_stop_thread_id = thread_id;
|
||||
if (windows_process.matching_pending_stop (debug_threads))
|
||||
return TRUE;
|
||||
|
||||
/* The inferior will only continue after the ContinueDebugEvent
|
||||
call. */
|
||||
for_each_thread ([&] (thread_info *thread)
|
||||
{
|
||||
continue_one_thread (thread, thread_id);
|
||||
});
|
||||
windows_process.faked_breakpoint = 0;
|
||||
|
||||
return continue_last_debug_event (continue_status, debug_threads);
|
||||
}
|
||||
@@ -452,8 +446,8 @@ child_fetch_inferior_registers (struct regcache *regcache, int r)
|
||||
{
|
||||
int regno;
|
||||
windows_thread_info *th
|
||||
= windows_process.thread_rec (current_thread_ptid (),
|
||||
INVALIDATE_CONTEXT);
|
||||
= windows_process.find_thread (current_thread_ptid ());
|
||||
win32_require_context (th);
|
||||
if (r == -1 || r > NUM_REGS)
|
||||
child_fetch_inferior_registers (regcache, NUM_REGS);
|
||||
else
|
||||
@@ -468,8 +462,8 @@ child_store_inferior_registers (struct regcache *regcache, int r)
|
||||
{
|
||||
int regno;
|
||||
windows_thread_info *th
|
||||
= windows_process.thread_rec (current_thread_ptid (),
|
||||
INVALIDATE_CONTEXT);
|
||||
= windows_process.find_thread (current_thread_ptid ());
|
||||
win32_require_context (th);
|
||||
if (r == -1 || r == 0 || r > NUM_REGS)
|
||||
child_store_inferior_registers (regcache, NUM_REGS);
|
||||
else
|
||||
@@ -631,9 +625,10 @@ win32_process_target::attach (unsigned long pid)
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
int
|
||||
bool
|
||||
gdbserver_windows_process::handle_output_debug_string
|
||||
(struct target_waitstatus *ourstatus)
|
||||
(const DEBUG_EVENT ¤t_event,
|
||||
struct target_waitstatus *ourstatus)
|
||||
{
|
||||
#define READ_BUFFER_LEN 1024
|
||||
CORE_ADDR addr;
|
||||
@@ -641,7 +636,7 @@ gdbserver_windows_process::handle_output_debug_string
|
||||
DWORD nbytes = current_event.u.DebugString.nDebugStringLength;
|
||||
|
||||
if (nbytes == 0)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
if (nbytes > READ_BUFFER_LEN)
|
||||
nbytes = READ_BUFFER_LEN;
|
||||
@@ -660,7 +655,7 @@ gdbserver_windows_process::handle_output_debug_string
|
||||
else
|
||||
{
|
||||
if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0)
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!startswith (s, "cYg"))
|
||||
@@ -668,14 +663,14 @@ gdbserver_windows_process::handle_output_debug_string
|
||||
if (!server_waiting)
|
||||
{
|
||||
OUTMSG2(("%s", s));
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
monitor_output (s);
|
||||
}
|
||||
#undef READ_BUFFER_LEN
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -688,7 +683,6 @@ win32_clear_inferiors (void)
|
||||
}
|
||||
|
||||
for_each_thread (delete_thread_info);
|
||||
windows_process.siginfo_er.ExceptionCode = 0;
|
||||
clear_inferiors ();
|
||||
}
|
||||
|
||||
@@ -700,16 +694,15 @@ win32_process_target::kill (process_info *process)
|
||||
TerminateProcess (windows_process.handle, 0);
|
||||
for (;;)
|
||||
{
|
||||
if (!child_continue (DBG_CONTINUE, -1))
|
||||
if (!child_continue_for_kill (DBG_CONTINUE, -1))
|
||||
break;
|
||||
if (!wait_for_debug_event (&windows_process.current_event, INFINITE))
|
||||
DEBUG_EVENT current_event;
|
||||
if (!wait_for_debug_event (¤t_event, INFINITE))
|
||||
break;
|
||||
if (windows_process.current_event.dwDebugEventCode
|
||||
== EXIT_PROCESS_DEBUG_EVENT)
|
||||
if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
|
||||
break;
|
||||
else if (windows_process.current_event.dwDebugEventCode
|
||||
== OUTPUT_DEBUG_STRING_EVENT)
|
||||
windows_process.handle_output_debug_string (nullptr);
|
||||
else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
|
||||
windows_process.handle_output_debug_string (current_event, nullptr);
|
||||
}
|
||||
|
||||
win32_clear_inferiors ();
|
||||
@@ -767,96 +760,108 @@ win32_process_target::thread_alive (ptid_t ptid)
|
||||
return find_thread_ptid (ptid) != NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
resume_one_thread (thread_info *thread, bool step, gdb_signal sig,
|
||||
DWORD *continue_status)
|
||||
{
|
||||
auto *th = (windows_thread_info *) thread_target_data (thread);
|
||||
|
||||
if (sig != GDB_SIGNAL_0)
|
||||
{
|
||||
/* Allow continuing with the same signal that interrupted us.
|
||||
Otherwise complain. */
|
||||
if (thread->id != get_last_debug_event_ptid ())
|
||||
{
|
||||
/* ContinueDebugEvent will be for a different thread. */
|
||||
OUTMSG (("Cannot continue with signal %d here. "
|
||||
"Not last-event thread", sig));
|
||||
}
|
||||
else if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
|
||||
{
|
||||
OUTMSG (("Cannot continue with signal %s here. "
|
||||
"Not stopped for EXCEPTION_DEBUG_EVENT.\n",
|
||||
gdb_signal_to_string (sig)));
|
||||
}
|
||||
else if (sig == th->last_sig)
|
||||
*continue_status = DBG_EXCEPTION_NOT_HANDLED;
|
||||
else
|
||||
OUTMSG (("Can only continue with received signal %s.\n",
|
||||
gdb_signal_to_string (th->last_sig)));
|
||||
}
|
||||
|
||||
win32_prepare_to_resume (th);
|
||||
|
||||
DWORD *context_flags;
|
||||
#ifdef __x86_64__
|
||||
if (windows_process.wow64_process)
|
||||
context_flags = &th->wow64_context.ContextFlags;
|
||||
else
|
||||
#endif
|
||||
context_flags = &th->context.ContextFlags;
|
||||
if (*context_flags)
|
||||
{
|
||||
/* Move register values from the inferior into the thread
|
||||
context structure. */
|
||||
regcache_invalidate ();
|
||||
|
||||
if (step)
|
||||
{
|
||||
if (the_low_target.single_step != NULL)
|
||||
(*the_low_target.single_step) (th);
|
||||
else
|
||||
error ("Single stepping is not supported "
|
||||
"in this configuration.\n");
|
||||
}
|
||||
|
||||
win32_set_thread_context (th);
|
||||
*context_flags = 0;
|
||||
}
|
||||
|
||||
continue_one_thread (thread, th->tid);
|
||||
}
|
||||
|
||||
/* Resume the inferior process. RESUME_INFO describes how we want
|
||||
to resume. */
|
||||
void
|
||||
win32_process_target::resume (thread_resume *resume_info, size_t n)
|
||||
{
|
||||
DWORD tid;
|
||||
enum gdb_signal sig;
|
||||
int step;
|
||||
windows_thread_info *th;
|
||||
DWORD continue_status = DBG_CONTINUE;
|
||||
ptid_t ptid;
|
||||
bool any_pending = false;
|
||||
|
||||
/* This handles the very limited set of resume packets that GDB can
|
||||
currently produce. */
|
||||
|
||||
if (n == 1 && resume_info[0].thread == minus_one_ptid)
|
||||
tid = -1;
|
||||
else if (n > 1)
|
||||
tid = -1;
|
||||
else
|
||||
/* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make
|
||||
the Windows resume code do the right thing for thread switching. */
|
||||
tid = windows_process.current_event.dwThreadId;
|
||||
|
||||
if (resume_info[0].thread != minus_one_ptid)
|
||||
for (thread_info *thread : all_threads)
|
||||
{
|
||||
sig = gdb_signal_from_host (resume_info[0].sig);
|
||||
step = resume_info[0].kind == resume_step;
|
||||
}
|
||||
else
|
||||
{
|
||||
sig = GDB_SIGNAL_0;
|
||||
step = 0;
|
||||
}
|
||||
auto *th = (windows_thread_info *) thread_target_data (thread);
|
||||
|
||||
if (sig != GDB_SIGNAL_0)
|
||||
{
|
||||
if (windows_process.current_event.dwDebugEventCode
|
||||
!= EXCEPTION_DEBUG_EVENT)
|
||||
for (int ndx = 0; ndx < n; ndx++)
|
||||
{
|
||||
OUTMSG (("Cannot continue with signal %s here.\n",
|
||||
gdb_signal_to_string (sig)));
|
||||
}
|
||||
else if (sig == windows_process.last_sig)
|
||||
continue_status = DBG_EXCEPTION_NOT_HANDLED;
|
||||
else
|
||||
OUTMSG (("Can only continue with received signal %s.\n",
|
||||
gdb_signal_to_string (windows_process.last_sig)));
|
||||
}
|
||||
thread_resume &r = resume_info[ndx];
|
||||
ptid_t ptid = r.thread;
|
||||
gdb_signal sig = gdb_signal_from_host (r.sig);
|
||||
bool step = r.kind == resume_step;
|
||||
|
||||
windows_process.last_sig = GDB_SIGNAL_0;
|
||||
|
||||
/* Get context for the currently selected thread. */
|
||||
ptid = debug_event_ptid (&windows_process.current_event);
|
||||
th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
|
||||
if (th)
|
||||
{
|
||||
win32_prepare_to_resume (th);
|
||||
|
||||
DWORD *context_flags;
|
||||
#ifdef __x86_64__
|
||||
if (windows_process.wow64_process)
|
||||
context_flags = &th->wow64_context.ContextFlags;
|
||||
else
|
||||
#endif
|
||||
context_flags = &th->context.ContextFlags;
|
||||
if (*context_flags)
|
||||
{
|
||||
/* Move register values from the inferior into the thread
|
||||
context structure. */
|
||||
regcache_invalidate ();
|
||||
|
||||
if (step)
|
||||
if (ptid == minus_one_ptid || ptid == thread->id
|
||||
/* Handle both 'pPID' and 'pPID.-1' as meaning 'all threads
|
||||
of PID'. */
|
||||
|| (ptid.pid () == pid_of (thread)
|
||||
&& (ptid.is_pid () || ptid.lwp () == -1)))
|
||||
{
|
||||
if (the_low_target.single_step != NULL)
|
||||
(*the_low_target.single_step) (th);
|
||||
else
|
||||
error ("Single stepping is not supported "
|
||||
"in this configuration.\n");
|
||||
}
|
||||
/* Ignore (wildcard) resume requests for already-resumed
|
||||
threads. */
|
||||
if (!th->suspended)
|
||||
continue;
|
||||
|
||||
win32_set_thread_context (th);
|
||||
*context_flags = 0;
|
||||
resume_one_thread (thread, step, sig, &continue_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!th->suspended
|
||||
&& th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
|
||||
any_pending = true;
|
||||
}
|
||||
|
||||
/* Allow continuing with the same signal that interrupted us.
|
||||
Otherwise complain. */
|
||||
|
||||
child_continue (continue_status, tid);
|
||||
if (!any_pending)
|
||||
continue_last_debug_event (continue_status, debug_threads);
|
||||
}
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
@@ -916,7 +921,7 @@ gdbserver_windows_process::handle_load_dll (const char *name, LPVOID base)
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
void
|
||||
gdbserver_windows_process::handle_unload_dll ()
|
||||
gdbserver_windows_process::handle_unload_dll (const DEBUG_EVENT ¤t_event)
|
||||
{
|
||||
CORE_ADDR load_addr =
|
||||
(CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
|
||||
@@ -936,23 +941,6 @@ suspend_one_thread (thread_info *thread)
|
||||
th->suspend ();
|
||||
}
|
||||
|
||||
static void
|
||||
fake_breakpoint_event (void)
|
||||
{
|
||||
OUTMSG2(("fake_breakpoint_event\n"));
|
||||
|
||||
windows_process.faked_breakpoint = 1;
|
||||
|
||||
memset (&windows_process.current_event, 0,
|
||||
sizeof (windows_process.current_event));
|
||||
windows_process.current_event.dwThreadId = windows_process.main_thread_id;
|
||||
windows_process.current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
|
||||
windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
|
||||
= EXCEPTION_BREAKPOINT;
|
||||
|
||||
for_each_thread (suspend_one_thread);
|
||||
}
|
||||
|
||||
/* See nat/windows-nat.h. */
|
||||
|
||||
bool
|
||||
@@ -967,20 +955,19 @@ gdbserver_windows_process::handle_access_violation
|
||||
PC. */
|
||||
|
||||
static void
|
||||
maybe_adjust_pc ()
|
||||
maybe_adjust_pc (const DEBUG_EVENT ¤t_event)
|
||||
{
|
||||
struct regcache *regcache = get_thread_regcache (current_thread, 1);
|
||||
child_fetch_inferior_registers (regcache, -1);
|
||||
|
||||
windows_thread_info *th
|
||||
= windows_process.thread_rec (current_thread_ptid (),
|
||||
DONT_INVALIDATE_CONTEXT);
|
||||
= windows_process.find_thread (current_thread_ptid ());
|
||||
th->stopped_at_software_breakpoint = false;
|
||||
|
||||
if (windows_process.current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
|
||||
&& ((windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
|
||||
if (current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
|
||||
&& ((current_event.u.Exception.ExceptionRecord.ExceptionCode
|
||||
== EXCEPTION_BREAKPOINT)
|
||||
|| (windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
|
||||
|| (current_event.u.Exception.ExceptionRecord.ExceptionCode
|
||||
== STATUS_WX86_BREAKPOINT))
|
||||
&& windows_process.child_initialization_done)
|
||||
{
|
||||
@@ -995,43 +982,39 @@ maybe_adjust_pc ()
|
||||
|
||||
static int
|
||||
get_child_debug_event (DWORD *continue_status,
|
||||
struct target_waitstatus *ourstatus)
|
||||
struct target_waitstatus *ourstatus,
|
||||
DEBUG_EVENT *current_event)
|
||||
{
|
||||
ptid_t ptid;
|
||||
|
||||
windows_process.last_sig = GDB_SIGNAL_0;
|
||||
ourstatus->set_spurious ();
|
||||
*continue_status = DBG_CONTINUE;
|
||||
|
||||
/* Check if GDB sent us an interrupt request. */
|
||||
check_remote_input_interrupt_request ();
|
||||
|
||||
DEBUG_EVENT *current_event = &windows_process.current_event;
|
||||
|
||||
if (windows_process.soft_interrupt_requested)
|
||||
{
|
||||
windows_process.soft_interrupt_requested = 0;
|
||||
fake_breakpoint_event ();
|
||||
goto gotevent;
|
||||
}
|
||||
|
||||
windows_process.attaching = 0;
|
||||
{
|
||||
std::optional<pending_stop> stop
|
||||
= windows_process.fetch_pending_stop (debug_threads);
|
||||
if (stop.has_value ())
|
||||
for (thread_info *thread : all_threads)
|
||||
{
|
||||
*ourstatus = stop->status;
|
||||
windows_process.current_event = stop->event;
|
||||
ptid = debug_event_ptid (&windows_process.current_event);
|
||||
switch_to_thread (find_thread_ptid (ptid));
|
||||
return 1;
|
||||
auto *th = (windows_thread_info *) thread_target_data (thread);
|
||||
|
||||
if (!th->suspended
|
||||
&& th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
|
||||
{
|
||||
*ourstatus = th->pending_status;
|
||||
th->pending_status.set_ignore ();
|
||||
*current_event = th->last_event;
|
||||
ptid = debug_event_ptid (current_event);
|
||||
switch_to_thread (find_thread_ptid (ptid));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep the wait time low enough for comfortable remote
|
||||
interruption, but high enough so gdbserver doesn't become a
|
||||
bottleneck. */
|
||||
if (!wait_for_debug_event (&windows_process.current_event, 250))
|
||||
if (!wait_for_debug_event (current_event, 250))
|
||||
{
|
||||
DWORD e = GetLastError();
|
||||
|
||||
@@ -1049,8 +1032,6 @@ get_child_debug_event (DWORD *continue_status,
|
||||
}
|
||||
}
|
||||
|
||||
gotevent:
|
||||
|
||||
switch (current_event->dwDebugEventCode)
|
||||
{
|
||||
case CREATE_THREAD_DEBUG_EVENT:
|
||||
@@ -1117,7 +1098,7 @@ get_child_debug_event (DWORD *continue_status,
|
||||
else
|
||||
ourstatus->set_signalled (gdb_signal_from_host (exit_signal));
|
||||
}
|
||||
child_continue (DBG_CONTINUE, windows_process.desired_stop_thread_id);
|
||||
continue_last_debug_event (DBG_CONTINUE, debug_threads);
|
||||
break;
|
||||
|
||||
case LOAD_DLL_DEBUG_EVENT:
|
||||
@@ -1128,7 +1109,7 @@ get_child_debug_event (DWORD *continue_status,
|
||||
CloseHandle (current_event->u.LoadDll.hFile);
|
||||
if (! windows_process.child_initialization_done)
|
||||
break;
|
||||
windows_process.dll_loaded_event ();
|
||||
windows_process.dll_loaded_event (*current_event);
|
||||
|
||||
ourstatus->set_loaded ();
|
||||
break;
|
||||
@@ -1140,7 +1121,7 @@ get_child_debug_event (DWORD *continue_status,
|
||||
(unsigned) current_event->dwThreadId));
|
||||
if (! windows_process.child_initialization_done)
|
||||
break;
|
||||
windows_process.handle_unload_dll ();
|
||||
windows_process.handle_unload_dll (*current_event);
|
||||
ourstatus->set_loaded ();
|
||||
break;
|
||||
|
||||
@@ -1149,7 +1130,8 @@ get_child_debug_event (DWORD *continue_status,
|
||||
"for pid=%u tid=%x\n",
|
||||
(unsigned) current_event->dwProcessId,
|
||||
(unsigned) current_event->dwThreadId));
|
||||
if (windows_process.handle_exception (ourstatus, debug_threads)
|
||||
if (windows_process.handle_exception (*current_event,
|
||||
ourstatus, debug_threads)
|
||||
== HANDLE_EXCEPTION_UNHANDLED)
|
||||
*continue_status = DBG_EXCEPTION_NOT_HANDLED;
|
||||
break;
|
||||
@@ -1160,7 +1142,7 @@ get_child_debug_event (DWORD *continue_status,
|
||||
"for pid=%u tid=%x\n",
|
||||
(unsigned) current_event->dwProcessId,
|
||||
(unsigned) current_event->dwThreadId));
|
||||
windows_process.handle_output_debug_string (nullptr);
|
||||
windows_process.handle_output_debug_string (*current_event, nullptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1172,19 +1154,22 @@ get_child_debug_event (DWORD *continue_status,
|
||||
break;
|
||||
}
|
||||
|
||||
ptid = debug_event_ptid (&windows_process.current_event);
|
||||
ptid = debug_event_ptid (current_event);
|
||||
|
||||
if (windows_process.desired_stop_thread_id != -1
|
||||
&& windows_process.desired_stop_thread_id != ptid.lwp ())
|
||||
windows_thread_info *th = windows_process.find_thread (ptid);
|
||||
|
||||
th->last_event = *current_event;
|
||||
|
||||
if (th != nullptr && th->suspended)
|
||||
{
|
||||
/* Pending stop. See the comment by the definition of
|
||||
"pending_stops" for details on why this is needed. */
|
||||
"windows_thread_info::pending_status" for details on why this
|
||||
is needed. */
|
||||
OUTMSG2 (("get_windows_debug_event - "
|
||||
"unexpected stop in 0x%lx (expecting 0x%x)\n",
|
||||
ptid.lwp (), windows_process.desired_stop_thread_id));
|
||||
maybe_adjust_pc ();
|
||||
windows_process.pending_stops.push_back
|
||||
({(DWORD) ptid.lwp (), *ourstatus, *current_event});
|
||||
"unexpected stop in suspended thread 0x%x\n",
|
||||
th->tid));
|
||||
maybe_adjust_pc (*current_event);
|
||||
th->pending_status = *ourstatus;
|
||||
ourstatus->set_spurious ();
|
||||
}
|
||||
else
|
||||
@@ -1208,13 +1193,16 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
|
||||
fails). Report it now. */
|
||||
*ourstatus = windows_process.cached_status;
|
||||
windows_process.cached_status.set_ignore ();
|
||||
return debug_event_ptid (&windows_process.current_event);
|
||||
return ptid_t (windows_process.process_id,
|
||||
windows_process.main_thread_id, 0);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD continue_status;
|
||||
if (!get_child_debug_event (&continue_status, ourstatus))
|
||||
DEBUG_EVENT current_event;
|
||||
if (!get_child_debug_event (&continue_status, ourstatus,
|
||||
¤t_event))
|
||||
continue;
|
||||
|
||||
switch (ourstatus->kind ())
|
||||
@@ -1223,15 +1211,20 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
|
||||
OUTMSG2 (("Child exited with retcode = %x\n",
|
||||
ourstatus->exit_status ()));
|
||||
win32_clear_inferiors ();
|
||||
return ptid_t (windows_process.current_event.dwProcessId);
|
||||
return ptid_t (windows_process.process_id);
|
||||
case TARGET_WAITKIND_STOPPED:
|
||||
case TARGET_WAITKIND_SIGNALLED:
|
||||
case TARGET_WAITKIND_LOADED:
|
||||
{
|
||||
OUTMSG2 (("Child Stopped with signal = %d \n",
|
||||
ourstatus->sig ()));
|
||||
maybe_adjust_pc ();
|
||||
return debug_event_ptid (&windows_process.current_event);
|
||||
maybe_adjust_pc (current_event);
|
||||
|
||||
/* All-stop, suspend all threads until they are explicitly
|
||||
resumed. */
|
||||
for_each_thread (suspend_one_thread);
|
||||
|
||||
return debug_event_ptid (¤t_event);
|
||||
}
|
||||
default:
|
||||
OUTMSG (("Ignoring unknown internal event, %d\n",
|
||||
@@ -1239,8 +1232,7 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
|
||||
[[fallthrough]];
|
||||
case TARGET_WAITKIND_SPURIOUS:
|
||||
/* do nothing, just continue */
|
||||
child_continue (continue_status,
|
||||
windows_process.desired_stop_thread_id);
|
||||
continue_last_debug_event (continue_status, debug_threads);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1298,8 +1290,7 @@ win32_process_target::request_interrupt ()
|
||||
if (DebugBreakProcess (windows_process.handle))
|
||||
return;
|
||||
|
||||
/* Last resort, suspend all threads manually. */
|
||||
windows_process.soft_interrupt_requested = 1;
|
||||
OUTMSG2 (("Could not interrupt.\n"));
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1322,45 +1313,14 @@ win32_process_target::qxfer_siginfo (const char *annex,
|
||||
unsigned const char *writebuf,
|
||||
CORE_ADDR offset, int len)
|
||||
{
|
||||
if (windows_process.siginfo_er.ExceptionCode == 0)
|
||||
windows_thread_info *th
|
||||
= windows_process.find_thread (current_thread_ptid ());
|
||||
|
||||
ULONGEST xfered_len;
|
||||
if (th->xfer_siginfo (readbuf, offset, len, &xfered_len))
|
||||
return xfered_len;
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (readbuf == nullptr)
|
||||
return -1;
|
||||
|
||||
char *buf = (char *) &windows_process.siginfo_er;
|
||||
size_t bufsize = sizeof (windows_process.siginfo_er);
|
||||
|
||||
#ifdef __x86_64__
|
||||
EXCEPTION_RECORD32 er32;
|
||||
if (windows_process.wow64_process)
|
||||
{
|
||||
buf = (char *) &er32;
|
||||
bufsize = sizeof (er32);
|
||||
|
||||
er32.ExceptionCode = windows_process.siginfo_er.ExceptionCode;
|
||||
er32.ExceptionFlags = windows_process.siginfo_er.ExceptionFlags;
|
||||
er32.ExceptionRecord
|
||||
= (uintptr_t) windows_process.siginfo_er.ExceptionRecord;
|
||||
er32.ExceptionAddress
|
||||
= (uintptr_t) windows_process.siginfo_er.ExceptionAddress;
|
||||
er32.NumberParameters = windows_process.siginfo_er.NumberParameters;
|
||||
int i;
|
||||
for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
|
||||
er32.ExceptionInformation[i]
|
||||
= windows_process.siginfo_er.ExceptionInformation[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (offset > bufsize)
|
||||
return -1;
|
||||
|
||||
if (offset + len > bufsize)
|
||||
len = bufsize - offset;
|
||||
|
||||
memcpy (readbuf, buf + offset, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1374,8 +1334,7 @@ win32_process_target::supports_get_tib_address ()
|
||||
int
|
||||
win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr)
|
||||
{
|
||||
windows_thread_info *th;
|
||||
th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
|
||||
windows_thread_info *th = windows_process.find_thread (ptid);
|
||||
if (th == NULL)
|
||||
return 0;
|
||||
if (addr != NULL)
|
||||
@@ -1396,8 +1355,7 @@ bool
|
||||
win32_process_target::stopped_by_sw_breakpoint ()
|
||||
{
|
||||
windows_thread_info *th
|
||||
= windows_process.thread_rec (current_thread_ptid (),
|
||||
DONT_INVALIDATE_CONTEXT);
|
||||
= windows_process.find_thread (current_thread_ptid ());
|
||||
return th == nullptr ? false : th->stopped_at_software_breakpoint;
|
||||
}
|
||||
|
||||
@@ -1423,8 +1381,7 @@ const char *
|
||||
win32_process_target::thread_name (ptid_t thread)
|
||||
{
|
||||
windows_thread_info *th
|
||||
= windows_process.thread_rec (current_thread_ptid (),
|
||||
DONT_INVALIDATE_CONTEXT);
|
||||
= windows_process.find_thread (current_thread_ptid ());
|
||||
return th->thread_name ();
|
||||
}
|
||||
|
||||
|
||||
@@ -174,14 +174,15 @@ public:
|
||||
|
||||
struct gdbserver_windows_process : public windows_nat::windows_process_info
|
||||
{
|
||||
windows_nat::windows_thread_info *thread_rec
|
||||
(ptid_t ptid,
|
||||
windows_nat::thread_disposition_type disposition) override;
|
||||
int handle_output_debug_string (struct target_waitstatus *ourstatus) override;
|
||||
windows_nat::windows_thread_info *find_thread (ptid_t ptid) override;
|
||||
bool handle_output_debug_string (const DEBUG_EVENT ¤t_event,
|
||||
struct target_waitstatus *ourstatus) override;
|
||||
void handle_load_dll (const char *dll_name, LPVOID base) override;
|
||||
void handle_unload_dll () override;
|
||||
void handle_unload_dll (const DEBUG_EVENT ¤t_event) override;
|
||||
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
|
||||
|
||||
void fill_thread_context (windows_nat::windows_thread_info *th) override;
|
||||
|
||||
int attaching = 0;
|
||||
|
||||
/* A status that hasn't been reported to the core yet, and so
|
||||
@@ -189,14 +190,6 @@ struct gdbserver_windows_process : public windows_nat::windows_process_info
|
||||
debug event off the win32 API. */
|
||||
struct target_waitstatus cached_status;
|
||||
|
||||
/* Non zero if an interrupt request is to be satisfied by suspending
|
||||
all threads. */
|
||||
int soft_interrupt_requested = 0;
|
||||
|
||||
/* Non zero if the inferior is stopped in a simulated breakpoint done
|
||||
by suspending all the threads. */
|
||||
int faked_breakpoint = 0;
|
||||
|
||||
/* True if current_process_handle needs to be closed. */
|
||||
bool open_process_used = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user