Fix handling DLL loads at run time
authorEli Zaretskii <eliz@gnu.org>
Sat, 10 Apr 2021 08:33:08 +0000 (11:33 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 10 Apr 2021 08:42:54 +0000 (11:42 +0300)
This patch makes handling a DLL load at run time (using LoadLibrary)
much more reliable when its file name cannot be obtained using the
lpImageName pointer provided by the DLL load debug event.  The
solution is to enumerate all the DLLs loaded by the inferior, looking
for the DLL that's loaded at base address provided by the lpBaseOfDll
pointer of the debug event.  Correctly resolving the DLL file name is
important, because without that GDB doesn't record the DLL in the list
of solibs, and then later is unable to show functions in that DLL in
the backtraces, which produces corrupted and truncated backtraces.
See this thread for the problems that causes:

  https://sourceware.org/pipermail/gdb-patches/2021-March/177022.html

gdb/ChangeLog:

2021-04-10  Eli Zaretskii  <eliz@gnu.org>

* windows-nat.c (windows_nat::handle_load_dll): Call
windows_add_dll if get_image_name failed to glean the name of the
DLL by using the lpImageName pointer.
(windows_add_all_dlls): Now a thin wrapper around windows_add_dll.
(windows_add_dll): Now does what windows_add_all_dlls did before,
but also accepts an argument LOAD_ADDR, which, if non-NULL,
specifies the address where the DLL was loaded into the inferior,
and looks for the single DLL loaded at that address.

gdb/ChangeLog
gdb/windows-nat.c

index c5ed593582b9d8d60f5f149a7458425494e6361a..5c38a4ed164bc0dfba948597b3dd05461ef2338c 100644 (file)
@@ -1,3 +1,14 @@
+2021-04-10  Eli Zaretskii  <eliz@gnu.org>
+
+       * windows-nat.c (windows_nat::handle_load_dll): Call
+       windows_add_dll if get_image_name failed to glean the name of the
+       DLL by using the lpImageName pointer.
+       (windows_add_all_dlls): Now a thin wrapper around windows_add_dll.
+       (windows_add_dll): Now does what windows_add_all_dlls did before,
+       but also accepts an argument LOAD_ADDR, which, if non-NULL,
+       specifies the address where the DLL was loaded into the inferior,
+       and looks for the single DLL loaded at that address.
+
 2021-04-09  Luis Machado  <luis.machado@linaro.org>
 
        * nat/aarch64-mte-linux-ptrace.c: Update include file order.
index 00f83536e330beb16daf6707eb910777a514dc13..c0f64f879108b1acb96e789e9b13788c564c98d4 100644 (file)
@@ -869,6 +869,8 @@ windows_make_so (const char *name, LPVOID load_addr)
   return so;
 }
 
+static bool windows_add_dll (LPVOID);
+
 /* See nat/windows-nat.h.  */
 
 void
@@ -884,11 +886,21 @@ windows_nat::handle_load_dll ()
      (source: MSDN LOAD_DLL_DEBUG_INFO structure).  */
   dll_name = get_image_name (current_process_handle,
                             event->lpImageName, event->fUnicode);
-  if (!dll_name)
-    return;
+  /* If the DLL name could not be gleaned via lpImageName, try harder
+     by enumerating all the DLLs loaded into the inferior, looking for
+     one that is loaded at base address = lpBaseOfDll. */
+  if (dll_name != nullptr)
+    {
 
-  solib_end->next = windows_make_so (dll_name, event->lpBaseOfDll);
-  solib_end = solib_end->next;
+      solib_end->next = windows_make_so (dll_name, event->lpBaseOfDll);
+      solib_end = solib_end->next;
+    }
+  else if (event->lpBaseOfDll != nullptr
+          && windows_add_dll (event->lpBaseOfDll))
+    dll_name = solib_end->so_name;
+
+  if (dll_name == nullptr)
+    return;
 
   lm_info_windows *li = (lm_info_windows *) solib_end->lm_info;
 
@@ -1898,6 +1910,19 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
 
 static void
 windows_add_all_dlls (void)
+{
+  windows_add_dll (NULL);
+}
+
+/* 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
+   list of solibs and return 'true'; otherwise do nothing and return
+   'false'.  LOAD_ADDR NULL means add all DLLs to the list of solibs;
+   this is used when the inferior finishes its initialization, and all
+   the DLLs it statically depends on are presumed loaded.  */
+
+static bool
+windows_add_dll (LPVOID load_addr)
 {
   HMODULE dummy_hmodule;
   DWORD cb_needed;
@@ -1910,18 +1935,18 @@ windows_add_all_dlls (void)
       if (EnumProcessModulesEx (current_process_handle, &dummy_hmodule,
                                sizeof (HMODULE), &cb_needed,
                                LIST_MODULES_32BIT) == 0)
-       return;
+       return false;
     }
   else
 #endif
     {
       if (EnumProcessModules (current_process_handle, &dummy_hmodule,
                              sizeof (HMODULE), &cb_needed) == 0)
-       return;
+       return false;
     }
 
   if (cb_needed < 1)
-    return;
+    return false;
 
   hmodules = (HMODULE *) alloca (cb_needed);
 #ifdef __x86_64__
@@ -1930,14 +1955,14 @@ windows_add_all_dlls (void)
       if (EnumProcessModulesEx (current_process_handle, hmodules,
                                cb_needed, &cb_needed,
                                LIST_MODULES_32BIT) == 0)
-       return;
+       return false;
     }
   else
 #endif
     {
       if (EnumProcessModules (current_process_handle, hmodules,
                              cb_needed, &cb_needed) == 0)
-       return;
+       return false;
     }
 
   char system_dir[__PMAX];
@@ -1983,6 +2008,7 @@ windows_add_all_dlls (void)
       if (GetModuleInformation (current_process_handle, hmodules[i],
                                &mi, sizeof (mi)) == 0)
        continue;
+
       if (GetModuleFileNameEx (current_process_handle, hmodules[i],
                               dll_name, sizeof (dll_name)) == 0)
        continue;
@@ -2005,9 +2031,17 @@ windows_add_all_dlls (void)
          name = syswow_dll_path.c_str();
        }
 
-      solib_end->next = windows_make_so (name, mi.lpBaseOfDll);
-      solib_end = solib_end->next;
+      /* Record the DLL if either LOAD_ADDR is NULL or the address
+        at which the DLL was loaded is equal to LOAD_ADDR.  */
+      if (!(load_addr != nullptr && mi.lpBaseOfDll != load_addr))
+       {
+         solib_end->next = windows_make_so (name, mi.lpBaseOfDll);
+         solib_end = solib_end->next;
+         if (load_addr != nullptr)
+           return true;
+       }
     }
+  return load_addr == nullptr ? true : false;
 }
 
 void