Explicitly LoadLibrary dbghelp.dll in symbol converter.

- This is a workaround to an issue with gn/clang build of Windows
symbol converter where dbghelp.dll is loaded from system32/syswow64
instead of alongside the process exe.
- Why do we care where dbghelp.dll is loaded from? Two considerations:
  1. dbghelp.dll will only load symsrv.dll from the directory where it
  resides.
  2. symsrv.dll requires a file called "symsrv.yes" to be in the
  directory where it resides in order to work with MS symbol stores.
Therefore if we load dbghelp.dll from syswow64, then we must also
ensure there is a symsrv.dll and symsrv.yes file in syswow64.

Change-Id: Ia283a2c11e276c855a48157aa7be77897af4b02e
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1680670
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
diff --git a/src/tools/windows/converter/ms_symbol_server_converter.cc b/src/tools/windows/converter/ms_symbol_server_converter.cc
index 4b0dcf6..2b40fae 100644
--- a/src/tools/windows/converter/ms_symbol_server_converter.cc
+++ b/src/tools/windows/converter/ms_symbol_server_converter.cc
@@ -36,6 +36,7 @@
 
 #include <windows.h>
 #include <dbghelp.h>
+#include <pathcch.h>
 
 #include <cassert>
 #include <cstdio>
@@ -53,6 +54,31 @@
 #define SYMOPT_NO_PROMPTS 0x00080000
 #endif  // SYMOPT_NO_PROMPTS
 
+namespace {
+
+std::wstring GetExeDirectory() {
+  wchar_t directory[MAX_PATH];
+
+  // Get path to this process exe.
+  DWORD result = GetModuleFileName(/*hModule=*/nullptr, directory, MAX_PATH);
+  if (result <= 0 || result == MAX_PATH) {
+    fprintf(stderr,
+        "GetExeDirectory: failed to get path to process exe.\n");
+    return L"";
+  }
+  HRESULT hr = PathCchRemoveFileSpec(directory, result + 1);
+  if (hr != S_OK) {
+    fprintf(stderr,
+        "GetExeDirectory: failed to remove basename from path '%ls'.\n",
+        directory);
+    return L"";
+  }
+
+  return std::wstring(directory);
+}
+
+}  // namespace
+
 namespace google_breakpad {
 
 // Use sscanf_s if it is available, to quench the warning about scanf being
@@ -160,6 +186,34 @@
 
   bool Initialize(HANDLE process, char *path, bool invade_process) {
     process_ = process;
+
+    // TODO(nbilling): Figure out why dbghelp.dll is being loaded from
+    // system32/SysWOW64 before exe folder.
+
+    // Attempt to locate and load dbghelp.dll beside the process exe. This is
+    // somewhat of a workaround to loader delay load behavior that is occurring
+    // when we call into symsrv APIs. dbghelp.dll must be loaded from beside
+    // the process exe so that we are guaranteed to find symsrv.dll alongside
+    // dbghelp.dll (a security requirement of dbghelp.dll) and so that the
+    // symsrv.dll file that is loaded has a symsrv.yes file alongside it (a
+    // requirement of symsrv.dll when accessing Microsoft-owned symbol
+    // servers).
+    // 'static local' because we don't care about the value but we need the
+    // initialization to happen exactly once.
+    static HMODULE dbghelp_module = [] () -> HMODULE {
+      std::wstring exe_directory = GetExeDirectory();
+      if (exe_directory.empty()) {
+        return nullptr;
+      }
+      std::wstring dbghelp_path = exe_directory + L"\\dbghelp.dll";
+      return LoadLibrary(dbghelp_path.c_str());
+    }();
+    if (dbghelp_module == nullptr) {
+      fprintf(stderr,
+          "AutoSymSrv::Initialize: failed to load dbghelp.dll beside exe.");
+      return false;
+    }
+
     initialized_ = SymInitialize(process, path, invade_process) == TRUE;
     return initialized_;
   }