Re: [RFA] nameless LOAD_DLL_DEBUG_EVENT causes ntdll.dll to be missing

Joel Brobecker
2013-12-13T15:45:46Z

[-- Attachment #1: Type: text/plain, Size: 466 bytes --]

> Looks OK to me. Thanks.

Thanks!

> Though it's harmless as is, there's no ntdll.dll on CE, only on
> Windows versions with NT based kernels. To be consistent
> with what we do elsewhere on the file already when
> treating ntdll.dll especially, this could be:
>
> #ifndef _WIN32_WCE
> win32_ensure_ntdll_loaded ();
> #endif

That's a good suggestion. Attached at the two patches I just pushed.
The second shows the added #ifndef's....

Thanks again!
--
Joel

[-- Attachment #2: 0001-Do-the-target-waiting-within-do_initial_child_stuff-.patch --]
[-- Type: text/x-diff, Size: 6765 bytes --]

From 4210d83ee607bffaf27a235f0475bf1e5ea8266d Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 12 Dec 2013 11:12:30 -0500
Subject: [PATCH 1/2] Do the target-waiting within do_initial_child_stuff on
Windows.

This is a preparatory patch that achieves two goals:

. Makes the initial event handling more similar to GDB's;
. Opens the door for implementing post-inititial-handling
operations.

At the moment, this is only done on Windows, where the
post-initial-handling is going to be needed (in the context of
Windows 2012). And because we're close to creating the gdb 7.7
branch, making that change for all platforms is a little more
risk that we'd like. So the change is currently implemented
on Windows.

gdb/gdbserver/ChangeLog:

* target.c (mywait): Set OURSTATUS->KIND to TARGET_WAITKIND_STOPPED
if equal to TARGET_WAITKIND_LOADED.
* win32-low.c (cached_status): New static global.
(win32_wait): Add declaration.
(do_initial_child_stuff): Flush all initial pending debug events
up to the initial breakpoint.
(win32_wait): If CACHED_STATUS was set, return that instead
of doing a real wait. Remove the code resuming the execution
of the inferior after receiving a TARGET_WAITKIND_LOADED event
during the initial phase. Also remove the code changing
OURSTATUS->KIND from TARGET_WAITKIND_LOADED to
TARGET_WAITKIND_STOPPED.
---
gdb/gdbserver/ChangeLog | 15 ++++++++++++
gdb/gdbserver/target.c | 5 ++++
gdb/gdbserver/win32-low.c | 61 +++++++++++++++++++++++++++++++++++------------
3 files changed, 66 insertions(+), 15 deletions(-)

diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 26d305e..9f8eb9e 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,18 @@
+2013-12-13 Pedro Alves <palves@redhat.com>
+
+ * target.c (mywait): Set OURSTATUS->KIND to TARGET_WAITKIND_STOPPED
+ if equal to TARGET_WAITKIND_LOADED.
+ * win32-low.c (cached_status): New static global.
+ (win32_wait): Add declaration.
+ (do_initial_child_stuff): Flush all initial pending debug events
+ up to the initial breakpoint.
+ (win32_wait): If CACHED_STATUS was set, return that instead
+ of doing a real wait. Remove the code resuming the execution
+ of the inferior after receiving a TARGET_WAITKIND_LOADED event
+ during the initial phase. Also remove the code changing
+ OURSTATUS->KIND from TARGET_WAITKIND_LOADED to
+ TARGET_WAITKIND_STOPPED.
+
2013-12-11 Yao Qi <yao@codesourcery.com>

* notif.c (handle_notif_ack): Return 0 if no notification
diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c
index d4a2a98..d229933 100644
--- a/gdb/gdbserver/target.c
+++ b/gdb/gdbserver/target.c
@@ -82,6 +82,11 @@ mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options,

ret = (*the_target->wait) (ptid, ourstatus, options);

+ /* We don't expose _LOADED events to gdbserver core. See the
+ `dlls_changed' global. */
+ if (ourstatus->kind == TARGET_WAITKIND_LOADED)
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
/* If GDB is connected through TCP/serial, then GDBserver will most
probably be running on its own terminal/console, so it's nice to
print there why is GDBserver exiting. If however, GDB is
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 979eedd..a4c9e77 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -80,6 +80,11 @@ static enum gdb_signal last_sig = GDB_SIGNAL_0;
/* The current debug event from WaitForDebugEvent. */
static DEBUG_EVENT current_event;

+/* A status that hasn't been reported to the core yet, and so
+ win32_wait should return it next, instead of fetching the next
+ debug event off the win32 API. */
+static struct target_waitstatus cached_status;
+
/* Non zero if an interrupt request is to be satisfied by suspending
all threads. */
static int soft_interrupt_requested = 0;
@@ -97,6 +102,8 @@ typedef BOOL (WINAPI *winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
typedef BOOL (WINAPI *winapi_DebugBreakProcess) (HANDLE);
typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);

+static ptid_t win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ int options);
static void win32_resume (struct thread_resume *resume_info, size_t n);

/* Get the thread ID from the current selected inferior (the current
@@ -336,6 +343,34 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)

if (the_low_target.initial_stuff != NULL)
(*the_low_target.initial_stuff) ();
+
+ cached_status.kind = TARGET_WAITKIND_IGNORE;
+
+ /* Flush all currently pending debug events (thread and dll list) up
+ to the initial breakpoint. */
+ while (1)
+ {
+ struct target_waitstatus status;
+
+ win32_wait (minus_one_ptid, &status, 0);
+
+ /* Note win32_wait doesn't return thread events. */
+ if (status.kind != TARGET_WAITKIND_LOADED)
+ {
+ cached_status = status;
+ break;
+ }
+
+ {
+ struct thread_resume resume;
+
+ resume.thread = minus_one_ptid;
+ resume.kind = resume_continue;
+ resume.sig = 0;
+
+ win32_resume (&resume, 1);
+ }
+ }
}

/* Resume all artificially suspended threads if we are continuing
@@ -1593,6 +1628,17 @@ win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
{
struct regcache *regcache;

+ if (cached_status.kind != TARGET_WAITKIND_IGNORE)
+ {
+ /* The core always does a wait after creating the inferior, and
+ do_initial_child_stuff already ran the inferior to the
+ initial breakpoint (or an exit, if creating the process
+ fails). Report it now. */
+ *ourstatus = cached_status;
+ cached_status.kind = TARGET_WAITKIND_IGNORE;
+ return debug_event_ptid (&current_event);
+ }
+
while (1)
{
if (!get_child_debug_event (ourstatus))
@@ -1612,21 +1658,6 @@ win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)

regcache = get_thread_regcache (current_inferior, 1);
child_fetch_inferior_registers (regcache, -1);
-
- if (ourstatus->kind == TARGET_WAITKIND_LOADED
- && !server_waiting)
- {
- /* When gdb connects, we want to be stopped at the
- initial breakpoint, not in some dll load event. */
- child_continue (DBG_CONTINUE, -1);
- break;
- }
-
- /* We don't expose _LOADED events to gdbserver core. See
- the `dlls_changed' global. */
- if (ourstatus->kind == TARGET_WAITKIND_LOADED)
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
-
return debug_event_ptid (&current_event);
default:
OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
--
1.8.1.2

[-- Attachment #3: 0002-nameless-LOAD_DLL_DEBUG_EVENT-causes-ntdll.dll-to-be.patch --]
[-- Type: text/x-diff, Size: 7812 bytes --]

From 379a5e2d36e4323702d48aeb794a8e42bf5bff5b Mon Sep 17 00:00:00 2001
From: Joel Brobecker <brobecker@adacore.com>
Date: Thu, 12 Dec 2013 12:53:45 -0500
Subject: [PATCH 2/2] nameless LOAD_DLL_DEBUG_EVENT causes ntdll.dll to be
missing

This is the gdbserver-equivalent of the change made in GDB to handle
the case, in x64 windows version 2012, where the kernel produces
a LOAD_DLL_DEBUG_EVENT where the name of the associated DLL cannot
be determined at that time, and thus has to be processed later.

The visible symptom is that ntdll.dll is missing from the list of
shared libraries known to be mapped by the inferior, with other
side-effects such as failure to unwind through code provided by
that DLL (such as exception handling routines).

gdb/gdbserver/ChangeLog:

* Makefile.in (safe-ctype.o, lbasename.o): New rules.
* configure.srv: Add safe-ctype.o and lbasename.o to srv_tgtobj
for all targets that use win32-low.c.
* win32-low.c (win32_ensure_ntdll_loaded): New function.
(do_initial_child_stuff): Add call to win32_ensure_ntdll_loaded.
---
gdb/gdbserver/ChangeLog | 8 +++++
gdb/gdbserver/Makefile.in | 6 ++++
gdb/gdbserver/configure.srv | 6 ++++
gdb/gdbserver/win32-low.c | 87 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 107 insertions(+)

diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 9f8eb9e..c73840f 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,11 @@
+2013-12-13 Joel Brobecker <brobecker@adacore.com>
+
+ * Makefile.in (safe-ctype.o, lbasename.o): New rules.
+ * configure.srv: Add safe-ctype.o and lbasename.o to srv_tgtobj
+ for all targets that use win32-low.c.
+ * win32-low.c (win32_ensure_ntdll_loaded): New function.
+ (do_initial_child_stuff): Add call to win32_ensure_ntdll_loaded.
+
2013-12-13 Pedro Alves <palves@redhat.com>

* target.c (mywait): Set OURSTATUS->KIND to TARGET_WAITKIND_STOPPED
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 641ea17..c8d971b 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -543,6 +543,12 @@ vasprintf.o: $(srcdir)/../../libiberty/vasprintf.c
vsnprintf.o: $(srcdir)/../../libiberty/vsnprintf.c
$(COMPILE) $<
$(POSTCOMPILE)
+safe-ctype.o: $(srcdir)/../../libiberty/safe-ctype.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)
+lbasename.o: $(srcdir)/../../libiberty/lbasename.c
+ $(COMPILE) $<
+ $(POSTCOMPILE)

aarch64.c : $(srcdir)/../regformats/aarch64.dat $(regdat_sh)
$(SHELL) $(regdat_sh) $(srcdir)/../regformats/aarch64.dat aarch64.c
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 32d935a..f4e6154 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -77,6 +77,7 @@ case "${target}" in
;;
arm*-*-mingw32ce*) srv_regobj=reg-arm.o
srv_tgtobj="win32-low.o win32-arm-low.o"
+ srv_tgtobj="${srv_tgtobj} safe-ctype.o lbasename.o"
srv_tgtobj="${srv_tgtobj} wincecompat.o"
# hostio_last_error implementation is in win32-low.c
srv_hostio_err_objs=""
@@ -100,6 +101,7 @@ case "${target}" in
;;
i[34567]86-*-cygwin*) srv_regobj="$srv_i386_regobj"
srv_tgtobj="i386-low.o win32-low.o win32-i386-low.o"
+ srv_tgtobj="${srv_tgtobj} safe-ctype.o lbasename.o"
srv_xmlfiles="$srv_i386_xmlfiles"
;;
i[34567]86-*-linux*) srv_regobj="$srv_i386_linux_regobj"
@@ -126,6 +128,7 @@ case "${target}" in
i[34567]86-*-mingw32ce*)
srv_regobj="$srv_i386_regobj"
srv_tgtobj="i386-low.o win32-low.o win32-i386-low.o"
+ srv_tgtobj="${srv_tgtobj} safe-ctype.o lbasename.o"
srv_tgtobj="${srv_tgtobj} wincecompat.o"
srv_xmlfiles="$srv_i386_xmlfiles"
# hostio_last_error implementation is in win32-low.c
@@ -135,6 +138,7 @@ case "${target}" in
;;
i[34567]86-*-mingw*) srv_regobj="$srv_i386_regobj"
srv_tgtobj="i386-low.o win32-low.o win32-i386-low.o"
+ srv_tgtobj="${srv_tgtobj} safe-ctype.o lbasename.o"
srv_xmlfiles="$srv_i386_xmlfiles"
srv_mingw=yes
;;
@@ -326,11 +330,13 @@ case "${target}" in
;;
x86_64-*-mingw*) srv_regobj="$srv_amd64_regobj"
srv_tgtobj="i386-low.o i387-fp.o win32-low.o win32-i386-low.o"
+ srv_tgtobj="${srv_tgtobj} safe-ctype.o lbasename.o"
srv_xmlfiles="$srv_i386_xmlfiles $srv_amd64_xmlfiles"
srv_mingw=yes
;;
x86_64-*-cygwin*) srv_regobj="$srv_amd64_regobj"
srv_tgtobj="i386-low.o i387-fp.o win32-low.o win32-i386-low.o"
+ srv_tgtobj="${srv_tgtobj} safe-ctype.o lbasename.o"
srv_xmlfiles="$srv_i386_xmlfiles"
;;

diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index a4c9e77..bcc77fb 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -105,6 +105,9 @@ typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
static ptid_t win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus,
int options);
static void win32_resume (struct thread_resume *resume_info, size_t n);
+#ifndef _WIN32_WCE
+static void win32_ensure_ntdll_loaded (void);
+#endif

/* Get the thread ID from the current selected inferior (the current
thread). */
@@ -371,6 +374,10 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
win32_resume (&resume, 1);
}
}
+
+#ifndef _WIN32_WCE
+ win32_ensure_ntdll_loaded ();
+#endif
}

/* Resume all artificially suspended threads if we are continuing
@@ -1134,6 +1141,86 @@ failed:
return 0;
}

+#ifndef _WIN32_WCE
+/* On certain versions of Windows, the information about ntdll.dll
+ is not available yet at the time we get the LOAD_DLL_DEBUG_EVENT,
+ thus preventing us from reporting this DLL as an SO. This has been
+ witnessed on Windows 8.1, for instance. A possible explanation
+ is that ntdll.dll might be mapped before the SO info gets created
+ by the Windows system -- ntdll.dll is the first DLL to be reported
+ via LOAD_DLL_DEBUG_EVENT and other DLLs do not seem to suffer from
+ that problem.
+
+ If we indeed are missing ntdll.dll, this function tries to recover
+ from this issue, after the fact. Do nothing if we encounter any
+ issue trying to locate that DLL. */
+
+static void
+win32_ensure_ntdll_loaded (void)
+{
+ struct inferior_list_entry *dll_e;
+ size_t i;
+ HMODULE dh_buf[1];
+ HMODULE *DllHandle = dh_buf;
+ DWORD cbNeeded;
+ BOOL ok;
+
+ for (dll_e = all_dlls.head; dll_e != NULL; dll_e = dll_e->next)
+ {
+ struct dll_info *dll = (struct dll_info *) dll_e;
+
+ if (strcasecmp (lbasename (dll->name), "ntdll.dll") == 0)
+ return;
+ }
+
+ if (!load_psapi ())
+ return;
+
+ cbNeeded = 0;
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ sizeof (HMODULE),
+ &cbNeeded);
+
+ if (!ok || !cbNeeded)
+ return;
+
+ DllHandle = (HMODULE *) alloca (cbNeeded);
+ if (!DllHandle)
+ return;
+
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ cbNeeded,
+ &cbNeeded);
+ if (!ok)
+ return;
+
+ for (i = 0; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
+ {
+ MODULEINFO mi;
+ char dll_name[MAX_PATH];
+
+ if (!(*win32_GetModuleInformation) (current_process_handle,
+ DllHandle[i],
+ &mi,
+ sizeof (mi)))
+ continue;
+ if ((*win32_GetModuleFileNameExA) (current_process_handle,
+ DllHandle[i],
+ dll_name,
+ MAX_PATH) == 0)
+ continue;
+ if (strcasecmp (lbasename (dll_name), "ntdll.dll") == 0)
+ {
+ win32_add_one_solib (dll_name,
+ (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll);
+ return;
+ }
+ }
+}
+#endif
+
typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
--
1.8.1.2