[go: nahoru, domu]

Ensure user32 and gdi32 do not load in the renderer process

The sandboxed renderer process is explicitly blocked from calling into
user32 and gdi32 as a result of the Win32k lockdown. As such, there's no
reason to even load those dlls in the renderer process. By delayloading
all of the dlls necessary to prevent user32 and gdi32 from ever loading
into the renderer process we can achieve significant performance
improvements by reducing memory usage in every process.

This change adds many delayloads to chrome.exe, chrome.dll, and
chrome_child.dll. The most important ones are user32, gdi32, shell32,
ole32, and shwlapi, but there's additional ones included because they
also reference one of the above so the entire chain needs to be done.

There's also delayloads added to a few different component dlls,
however this change unfortunately does not completely fix the delayloads
in a component build because the the base_win_linker_flags cannot be
overridden and the sbox_integration_tests and sbox_validation_tests both
require the dlls to be statically linked. The reason they must be
statically linked is because of an OS bug related to the delayload
handler, as described below. It is prohibitively complicated to add the
delayloads to every single dll that depends on base, which is why it
hasn't been done.

One of the biggest considerations for this change is that there is an OS
bug in all versions of Windows which causes LoadLibrary to fail and the
delayload handler to crash with error code c06d007e
(ERROR_MOD_NOT_FOUND) when they run inside the sandboxed
process. The issue here is that there is an access check that fails due
to the ACL restrictions of the sandboxed process. While this OS bug is
unfortunate, it doesn't prevent this change from occurring; rather it
just requires that this change ensures no code executes which results
in the delayload handler from running. As such, this change adds calls
to IsUser32AndGdi32Available and updates all the calls that use
GetProcAddress into user32.dll to do proper LoadLibrary calls first.
To help make this cleaner, easier, and more consistent, two new methods
were created. First, PinUser32() will load user32.dll if not loaded and
ensure it is pinned (using a new PinSystemLibrary method) and cannot be
unloaded (as there's no good reason to let it unload). It also caches
the module handle to avoid subsequent calls to either GetModuleHandleEx
or LoadLibraryEx as those can be potentially blocking and are definitely
unnecessary once the module is pinned. The second method is
GetUser32FunctionPointer which simplifies the process of callers
retrieving function pointers by wrapping and hiding the LoadLibrary
requirements.

The OS bug which causes a crash actually helped with validation, as this
allowed for longhaul testing on private builds looking specifically for
crashes. This resulted in updates to this change as well as
separate changes that have been made to blink, pdfium, and v8 to remove
invalid method calls and dll references.

A critical part of this change is the update to
CommandLine::ParseFromString(). In this change the command line handler
explicitly loads api-ms-win-downlevel-shell32-l1-1-0.dll and finds
CommandLineToArgvW via the apiset if possible. This is necessary because
the standard delayload handler would normally find this method in
shell32, however loading shell32 brings in user32 and this prevents the
entire change from working. However, the actual implementation is in
shcore.dll, not shell32.dll, and the apiset is able to do this
forwarding and avoid shell32.dll entirely. This only works on Win10,
unfortunately, which is one reason why the performance benefits of this
change can only be accomplished on Win10, not Win7. On Win7, the
organization and linking of the system dlls will always bring in
user32.dll.

Finally, this change also adds a new test library, delayloads_unittests,
which is modeled after the existing chrome_elf_import_unittests, which
ensures the lists of statically linked dlls for the three main binaries
cannot be accidentally updated to break this change and it ensures the
dlls can be properly loaded without bringing in user32.

We've run some perf comparisons on this change and our findings show a
reduction in private COW pages (actual, committed private memory) of
~500K per renderer process!

Bug: 948829
Change-Id: I9eab10508866bc09e2f78abbe42e51d67f417186
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1551709
Reviewed-by: Greg Thompson <grt@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Bruce Dawson <brucedawson@chromium.org>
Reviewed-by: François Doray <fdoray@chromium.org>
Commit-Queue: Cliff Smolinsky <cliffsmo@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#656516}
diff --git a/base/command_line.cc b/base/command_line.cc
index 0bd071bb..3f13db20 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -438,7 +438,23 @@
 
   int num_args = 0;
   wchar_t** args = NULL;
-  args = ::CommandLineToArgvW(as_wcstr(command_line), &num_args);
+  // When calling CommandLineToArgvW, use the apiset if available.
+  // Doing so will bypass loading shell32.dll on Win8+.
+  HMODULE downlevel_shell32_dll =
+      ::LoadLibraryEx(L"api-ms-win-downlevel-shell32-l1-1-0.dll", nullptr,
+                      LOAD_LIBRARY_SEARCH_SYSTEM32);
+  if (downlevel_shell32_dll) {
+    auto command_line_to_argv_w_proc =
+        reinterpret_cast<decltype(::CommandLineToArgvW)*>(
+            ::GetProcAddress(downlevel_shell32_dll, "CommandLineToArgvW"));
+    if (command_line_to_argv_w_proc)
+      args = command_line_to_argv_w_proc(as_wcstr(command_line), &num_args);
+    ::FreeLibrary(downlevel_shell32_dll);
+  } else {
+    // Since the apiset is not available, allow the delayload of shell32.dll
+    // to take place.
+    args = ::CommandLineToArgvW(as_wcstr(command_line), &num_args);
+  }
 
   DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
                          << UTF16ToUTF8(command_line);
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index bfc1122..45f78a2 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -23,6 +23,7 @@
 
 #if defined(OS_WIN)
 #include <windows.h>
+#include "base/win/win_util.h"
 #elif defined(OS_MACOSX)
 #include <CoreFoundation/CoreFoundation.h>
 #endif
@@ -707,10 +708,11 @@
 
 int FilePath::CompareIgnoreCase(StringPieceType string1,
                                 StringPieceType string2) {
-  static decltype(::CharUpperW)* const char_upper_api =
-      reinterpret_cast<decltype(::CharUpperW)*>(
-          ::GetProcAddress(::GetModuleHandle(L"user32.dll"), "CharUpperW"));
-  CHECK(char_upper_api);
+  // CharUpperW within user32 is used here because it will provide unicode
+  // conversions regardless of locale. The STL alternative, towupper, has a
+  // locale consideration that prevents it from converting all characters by
+  // default.
+  CHECK(win::IsUser32AndGdi32Available());
   // Perform character-wise upper case comparison rather than using the
   // fully Unicode-aware CompareString(). For details see:
   // http://blogs.msdn.com/michkap/archive/2005/10/17/481600.aspx
@@ -720,9 +722,9 @@
   StringPieceType::const_iterator string2end = string2.end();
   for ( ; i1 != string1end && i2 != string2end; ++i1, ++i2) {
     wchar_t c1 =
-        (wchar_t)LOWORD(char_upper_api((LPWSTR)(DWORD_PTR)MAKELONG(*i1, 0)));
+        (wchar_t)LOWORD(::CharUpperW((LPWSTR)(DWORD_PTR)MAKELONG(*i1, 0)));
     wchar_t c2 =
-        (wchar_t)LOWORD(char_upper_api((LPWSTR)(DWORD_PTR)MAKELONG(*i2, 0)));
+        (wchar_t)LOWORD(::CharUpperW((LPWSTR)(DWORD_PTR)MAKELONG(*i2, 0)));
     if (c1 < c2)
       return -1;
     if (c1 > c2)
diff --git a/base/logging.cc b/base/logging.cc
index 79afe25b..bd9e2d8 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -107,6 +107,10 @@
 #include "base/threading/platform_thread.h"
 #include "base/vlog.h"
 
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
 #include "base/posix/safe_strerror.h"
 #endif
@@ -550,8 +554,12 @@
 #if defined(OS_WIN)
   // We intentionally don't implement a dialog on other platforms.
   // You can just look at stderr.
-  MessageBoxW(nullptr, base::UTF8ToUTF16(str).c_str(), L"Fatal error",
-              MB_OK | MB_ICONHAND | MB_TOPMOST);
+  if (base::win::IsUser32AndGdi32Available()) {
+    MessageBoxW(nullptr, base::as_wcstr(base::UTF8ToUTF16(str)), L"Fatal error",
+                MB_OK | MB_ICONHAND | MB_TOPMOST);
+  } else {
+    OutputDebugStringW(base::as_wcstr(base::UTF8ToUTF16(str)));
+  }
 #endif  // defined(OS_WIN)
 }
 #endif  // !defined(NDEBUG)
diff --git a/base/native_library.h b/base/native_library.h
index 4a65ffe..ec47c14 100644
--- a/base/native_library.h
+++ b/base/native_library.h
@@ -85,10 +85,20 @@
 #if defined(OS_WIN)
 // Loads a native library from the system directory using the appropriate flags.
 // The function first checks to see if the library is already loaded and will
-// get a handle if so. Blocking may occur if the library is not loaded and
-// LoadLibrary must be called.
-BASE_EXPORT NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
-                                            NativeLibraryLoadError* error);
+// get a handle if so. This method results in a lock that may block the calling
+// thread.
+BASE_EXPORT NativeLibrary
+LoadSystemLibrary(FilePath::StringPieceType name,
+                  NativeLibraryLoadError* error = nullptr);
+
+// Gets the module handle for the specified system library and pins it to
+// ensure it never gets unloaded. If the module is not loaded, it will first
+// call LoadSystemLibrary to load it. If the module cannot be pinned, this
+// method returns null and includes the error. This method results in a lock
+// that may block the calling thread.
+BASE_EXPORT NativeLibrary
+PinSystemLibrary(FilePath::StringPieceType name,
+                 NativeLibraryLoadError* error = nullptr);
 #endif
 
 // Loads a native library from disk.  Release it with UnloadNativeLibrary when
diff --git a/base/native_library_win.cc b/base/native_library_win.cc
index 47814eeed..08d520d0 100644
--- a/base/native_library_win.cc
+++ b/base/native_library_win.cc
@@ -112,7 +112,7 @@
     // GetLastError() needs to be called immediately after
     // LoadLibraryExW call.
     if (error)
-      error->code = GetLastError();
+      error->code = ::GetLastError();
   }
 
   // If LoadLibraryExW API/flags are unavailable or API call fails, try
@@ -133,7 +133,7 @@
 
   // GetLastError() needs to be called immediately after LoadLibraryW call.
   if (!module && error)
-    error->code = GetLastError();
+    error->code = ::GetLastError();
 
   if (restore_directory)
     SetCurrentDirectory(current_directory);
@@ -147,15 +147,15 @@
 
 NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
                                       NativeLibraryLoadError* error) {
+  // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
+  // hence must not be called from Dllmain.
+  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
   NativeLibrary module;
   BOOL module_found =
-      ::GetModuleHandleEx(0, as_wcstr(library_path.value()), &module);
+      ::GetModuleHandleExW(0, as_wcstr(library_path.value()), &module);
   if (!module_found) {
-    // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
-    // must not be called from DllMain.
-    ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
     bool are_search_flags_available = AreSearchFlagsAvailable();
-    // prefer LOAD_LIBRARY_SEARCH_SYSTEM32 to avoid DLL preloading attacks
+    // Prefer LOAD_LIBRARY_SEARCH_SYSTEM32 to avoid DLL preloading attacks.
     DWORD flags = are_search_flags_available ? LOAD_LIBRARY_SEARCH_SYSTEM32
                                              : LOAD_WITH_ALTERED_SEARCH_PATH;
     module = ::LoadLibraryExW(as_wcstr(library_path.value()), nullptr, flags);
@@ -218,4 +218,38 @@
   return nullptr;
 }
 
+NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
+                               NativeLibraryLoadError* error) {
+  Optional<FilePath> library_path = GetSystemLibraryName(name);
+  if (!library_path) {
+    if (error)
+      error->code = ERROR_NOT_FOUND;
+    return nullptr;
+  }
+
+  // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
+  // Dllmain.
+  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
+  ScopedNativeLibrary module;
+  if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
+                            as_wcstr(library_path.value().value()),
+                            ScopedNativeLibrary::Receiver(module).get())) {
+    // Load and pin the library since it wasn't already loaded.
+    module = ScopedNativeLibrary(
+        LoadSystemLibraryHelper(library_path.value(), error));
+    if (module.is_valid()) {
+      ScopedNativeLibrary temp;
+      if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
+                                as_wcstr(library_path.value().value()),
+                                ScopedNativeLibrary::Receiver(temp).get())) {
+        if (error)
+          error->code = ::GetLastError();
+        // Return nullptr since we failed to pin the module.
+        return nullptr;
+      }
+    }
+  }
+  return module.release();
+}
+
 }  // namespace base
diff --git a/base/win/win_util.cc b/base/win/win_util.cc
index 8bfe84d9..94e70637 100644
--- a/base/win/win_util.cc
+++ b/base/win/win_util.cc
@@ -99,16 +99,19 @@
 // Windows versions GetProcAddress will return null and report failure so that
 // callers can fall back on the deprecated SetProcessDPIAware.
 bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) {
-  decltype(&::SetProcessDpiAwareness) set_process_dpi_awareness_func =
-      reinterpret_cast<decltype(&::SetProcessDpiAwareness)>(GetProcAddress(
-          GetModuleHandle(L"user32.dll"), "SetProcessDpiAwarenessInternal"));
+  if (!IsUser32AndGdi32Available())
+    return false;
+
+  static const auto set_process_dpi_awareness_func =
+      reinterpret_cast<decltype(&::SetProcessDpiAwareness)>(
+          GetUser32FunctionPointer("SetProcessDpiAwarenessInternal"));
   if (set_process_dpi_awareness_func) {
     HRESULT hr = set_process_dpi_awareness_func(value);
     if (SUCCEEDED(hr))
       return true;
     DLOG_IF(ERROR, hr == E_ACCESSDENIED)
-        << "Access denied error from SetProcessDpiAwarenessInternal. Function "
-           "called twice, or manifest was used.";
+        << "Access denied error from SetProcessDpiAwarenessInternal. "
+           "Function called twice, or manifest was used.";
     NOTREACHED()
         << "SetProcessDpiAwarenessInternal failed with unexpected error: "
         << hr;
@@ -127,11 +130,12 @@
 // available (i.e., prior to Windows 10 1703) or fails, returns false.
 // https://docs.microsoft.com/en-us/windows/desktop/hidpi/dpi-awareness-context
 bool EnablePerMonitorV2() {
-  decltype(
-      &::SetProcessDpiAwarenessContext) set_process_dpi_awareness_context_func =
+  if (!IsUser32AndGdi32Available())
+    return false;
+
+  static const auto set_process_dpi_awareness_context_func =
       reinterpret_cast<decltype(&::SetProcessDpiAwarenessContext)>(
-          ::GetProcAddress(::GetModuleHandle(L"user32.dll"),
-                           "SetProcessDpiAwarenessContext"));
+          GetUser32FunctionPointer("SetProcessDpiAwarenessContext"));
   if (set_process_dpi_awareness_context_func) {
     return set_process_dpi_awareness_context_func(
         DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
@@ -174,6 +178,15 @@
   return &state;
 }
 
+NativeLibrary PinUser32Internal(NativeLibraryLoadError* error) {
+  static NativeLibraryLoadError load_error;
+  static const NativeLibrary user32_module =
+      PinSystemLibrary(FILE_PATH_LITERAL("user32.dll"), &load_error);
+  if (!user32_module && error)
+    error->code = load_error.code;
+  return user32_module;
+}
+
 }  // namespace
 
 // Uses the Windows 10 WRL API's to query the current system state. The API's
@@ -273,11 +286,8 @@
   //    if we find ACPI\* or HID\VID* keyboards.
 
   typedef BOOL (WINAPI* GetAutoRotationState)(PAR_STATE state);
-
-  GetAutoRotationState get_rotation_state =
-      reinterpret_cast<GetAutoRotationState>(::GetProcAddress(
-          GetModuleHandle(L"user32.dll"), "GetAutoRotationState"));
-
+  static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
+      GetUser32FunctionPointer("GetAutoRotationState"));
   if (get_rotation_state) {
     AR_STATE auto_rotation_state = AR_ENABLED;
     get_rotation_state(&auto_rotation_state);
@@ -287,8 +297,8 @@
       // the current configuration, then we can assume that this is a desktop
       // or a traditional laptop.
       if (reason) {
-        *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n" :
-                                                         "AR_NOT_SUPPORTED\n";
+        *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
+                                                       : "AR_NOT_SUPPORTED\n";
         result = true;
       } else {
         return true;
@@ -552,10 +562,9 @@
   // See
   // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
   typedef decltype(GetAutoRotationState)* GetAutoRotationStateType;
-  GetAutoRotationStateType get_auto_rotation_state_func =
-      reinterpret_cast<GetAutoRotationStateType>(GetProcAddress(
-          GetModuleHandle(L"user32.dll"), "GetAutoRotationState"));
-
+  static const auto get_auto_rotation_state_func =
+      reinterpret_cast<GetAutoRotationStateType>(
+          GetUser32FunctionPointer("GetAutoRotationState"));
   if (get_auto_rotation_state_func) {
     AR_STATE rotation_state = AR_ENABLED;
     if (get_auto_rotation_state_func(&rotation_state) &&
@@ -703,13 +712,16 @@
 }
 
 void EnableHighDPISupport() {
+  if (!IsUser32AndGdi32Available())
+    return;
+
   // Enable per-monitor V2 if it is available (Win10 1703 or later).
   if (EnablePerMonitorV2())
     return;
 
-  // Fall back to per-monitor DPI for older versions of Win10 instead of Win8.1
-  // since Win8.1 does not have EnableChildWindowDpiMessage, necessary for
-  // correct non-client area scaling across monitors.
+  // Fall back to per-monitor DPI for older versions of Win10 instead of
+  // Win8.1 since Win8.1 does not have EnableChildWindowDpiMessage,
+  // necessary for correct non-client area scaling across monitors.
   PROCESS_DPI_AWARENESS process_dpi_awareness =
       GetVersion() >= Version::WIN10 ? PROCESS_PER_MONITOR_DPI_AWARE
                                      : PROCESS_SYSTEM_DPI_AWARE;
@@ -736,6 +748,18 @@
   return string16(as_u16cstr(guid_string), kGuidStringCharacters - 1);
 }
 
+bool PinUser32(NativeLibraryLoadError* error) {
+  return PinUser32Internal(error) != nullptr;
+}
+
+void* GetUser32FunctionPointer(const char* function_name,
+                               NativeLibraryLoadError* error) {
+  NativeLibrary user32_module = PinUser32Internal(error);
+  if (user32_module)
+    return GetFunctionPointerFromNativeLibrary(user32_module, function_name);
+  return nullptr;
+}
+
 ScopedDomainStateForTesting::ScopedDomainStateForTesting(bool state)
     : initial_state_(IsEnrolledToDomain()) {
   *GetDomainEnrollmentStateStorage() = state;
diff --git a/base/win/win_util.h b/base/win/win_util.h
index fcbca51..e53f6d6 100644
--- a/base/win/win_util.h
+++ b/base/win/win_util.h
@@ -37,6 +37,9 @@
 typedef _tagpropertykey PROPERTYKEY;
 
 namespace base {
+
+struct NativeLibraryLoadError;
+
 namespace win {
 
 inline uint32_t HandleToUint32(HANDLE h) {
@@ -160,9 +163,17 @@
 
 // Returns true if the current process can make USER32 or GDI32 calls such as
 // CreateWindow and CreateDC. Windows 8 and above allow the kernel component
-// of these calls to be disabled which can cause undefined behaviour such as
-// crashes. This function can be used to guard areas of code using these calls
-// and provide a fallback path if necessary.
+// of these calls to be disabled (also known as win32k lockdown) which can
+// cause undefined behaviour such as crashes. This function can be used to
+// guard areas of code using these calls and provide a fallback path if
+// necessary.
+// Because they are not always needed (and not needed at all in processes that
+// have the win32k lockdown), USER32 and GDI32 are delayloaded. Attempts to
+// load them in those processes will cause a crash. Any code which uses USER32
+// or GDI32 and may run in a locked-down process MUST be guarded using this
+// method. Before the dlls were delayloaded, method calls into USER32 and GDI32
+// did not work, so adding calls to this method to guard them simply avoids
+// unnecessary method calls.
 BASE_EXPORT bool IsUser32AndGdi32Available();
 
 // Takes a snapshot of the modules loaded in the |process|. The returned
@@ -187,6 +198,21 @@
 // Returns a string representation of |rguid|.
 BASE_EXPORT string16 String16FromGUID(REFGUID rguid);
 
+// Attempts to pin user32.dll to ensure it remains loaded. If it isn't loaded
+// yet, the module will first be loaded and then the pin will be attempted. If
+// pinning is successful, returns true. If the module cannot be loaded and/or
+// pinned, |error| is set and the method returns false.
+BASE_EXPORT bool PinUser32(NativeLibraryLoadError* error = nullptr);
+
+// Gets a pointer to a function within user32.dll, if available. If user32.dll
+// cannot be loaded or the function cannot be found, this function returns
+// nullptr and sets |error|. Once loaded, user32.dll is pinned, and therefore
+// the function pointer returned by this function will never change and can be
+// cached.
+BASE_EXPORT void* GetUser32FunctionPointer(
+    const char* function_name,
+    NativeLibraryLoadError* error = nullptr);
+
 // Allows changing the domain enrolled state for the life time of the object.
 // The original state is restored upon destruction.
 class BASE_EXPORT ScopedDomainStateForTesting {
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 5c1f067..ef1dd234 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -236,12 +236,26 @@
         }
 
         ldflags = [
+          "/DELAYLOAD:advapi32.dll",
+          "/DELAYLOAD:comdlg32.dll",
           "/DELAYLOAD:dbghelp.dll",
           "/DELAYLOAD:dwmapi.dll",
-          "/DELAYLOAD:uxtheme.dll",
+          "/DELAYLOAD:imm32.dll",
+          "/DELAYLOAD:iphlpapi.dll",
           "/DELAYLOAD:ole32.dll",
           "/DELAYLOAD:oleaut32.dll",
+          "/DELAYLOAD:propsys.dll",
+          "/DELAYLOAD:psapi.dll",
+          "/DELAYLOAD:shell32.dll",
+          "/DELAYLOAD:shlwapi.dll",
+          "/DELAYLOAD:user32.dll",
+          "/DELAYLOAD:uxtheme.dll",
+          "/DELAYLOAD:wer.dll",
+          "/DELAYLOAD:wevtapi.dll",
           "/DELAYLOAD:winhttp.dll",
+          "/DELAYLOAD:wininet.dll",
+          "/DELAYLOAD:winmm.dll",
+          "/DELAYLOAD:wintrust.dll",
         ]
 
         if (current_cpu == "x64") {
@@ -421,22 +435,53 @@
     ]
 
     ldflags = [
+      "/DELAYLOAD:advapi32.dll",
+      "/DELAYLOAD:comctl32.dll",
       "/DELAYLOAD:comdlg32.dll",
+      "/DELAYLOAD:credui.dll",
       "/DELAYLOAD:crypt32.dll",
       "/DELAYLOAD:cryptui.dll",
+      "/DELAYLOAD:d3d11.dll",
+      "/DELAYLOAD:d3d9.dll",
       "/DELAYLOAD:dbghelp.dll",
       "/DELAYLOAD:dhcpcsvc.dll",
       "/DELAYLOAD:dwmapi.dll",
+      "/DELAYLOAD:dwrite.dll",
+      "/DELAYLOAD:dxgi.dll",
+      "/DELAYLOAD:esent.dll",
+      "/DELAYLOAD:gdi32.dll",
+      "/DELAYLOAD:hid.dll",
       "/DELAYLOAD:imagehlp.dll",
       "/DELAYLOAD:imm32.dll",
       "/DELAYLOAD:iphlpapi.dll",
+      "/DELAYLOAD:netapi32.dll",
+      "/DELAYLOAD:ole32.dll",
+      "/DELAYLOAD:oleacc.dll",
+      "/DELAYLOAD:oleaut32.dll",
+      "/DELAYLOAD:ncrypt.dll",
+      "/DELAYLOAD:propsys.dll",
+      "/DELAYLOAD:psapi.dll",
+      "/DELAYLOAD:secur32.dll",
       "/DELAYLOAD:setupapi.dll",
+      "/DELAYLOAD:shell32.dll",
+      "/DELAYLOAD:shlwapi.dll",
+      "/DELAYLOAD:uiautomationcore.dll",
       "/DELAYLOAD:urlmon.dll",
+      "/DELAYLOAD:user32.dll",
+      "/DELAYLOAD:userenv.dll",
+      "/DELAYLOAD:usp10.dll",
+      "/DELAYLOAD:wer.dll",
+      "/DELAYLOAD:wevtapi.dll",
       "/DELAYLOAD:winhttp.dll",
       "/DELAYLOAD:wininet.dll",
+      "/DELAYLOAD:winmm.dll",
       "/DELAYLOAD:winspool.drv",
+      "/DELAYLOAD:wintrust.dll",
+      "/DELAYLOAD:winusb.dll",
       "/DELAYLOAD:ws2_32.dll",
       "/DELAYLOAD:wsock32.dll",
+      "/DELAYLOAD:wtsapi32.dll",
+      "/DELAYLOAD:api-ms-win-core-winrt-string-l1-1-0.dll",
     ]
 
     if (!is_component_build) {
@@ -520,11 +565,25 @@
       ]
 
       ldflags = [
+        "/DELAYLOAD:comctl32.dll",
+        "/DELAYLOAD:comdlg32.dll",
         "/DELAYLOAD:d3d11.dll",
         "/DELAYLOAD:d3d9.dll",
         "/DELAYLOAD:dwmapi.dll",
+        "/DELAYLOAD:dxgi.dll",
         "/DELAYLOAD:dxva2.dll",
         "/DELAYLOAD:esent.dll",
+        "/DELAYLOAD:gdi32.dll",
+        "/DELAYLOAD:imm32.dll",
+        "/DELAYLOAD:ole32.dll",
+        "/DELAYLOAD:oleacc.dll",
+        "/DELAYLOAD:propsys.dll",
+        "/DELAYLOAD:rstrtmgr.dll",
+        "/DELAYLOAD:shell32.dll",
+        "/DELAYLOAD:shlwapi.dll",
+        "/DELAYLOAD:urlmon.dll",
+        "/DELAYLOAD:user32.dll",
+        "/DELAYLOAD:usp10.dll",
         "/DELAYLOAD:wininet.dll",
       ]
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c3eb837..b6ce953 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6095,3 +6095,40 @@
     generate_python = false
   }
 }
+
+if (is_win) {
+  test("delayloads_unittests") {
+    output_name = "delayloads_unittests"
+    sources = [
+      "delayload/delayloads_unittest.cc",
+    ]
+    include_dirs = [ "$target_gen_dir" ]
+    deps = [
+      "//base",
+      "//base/test:test_support",
+      "//chrome",
+      "//chrome/install_static:install_static_util",
+      "//chrome/install_static/test:test_support",
+      "//testing/gtest",
+    ]
+
+    # It's not easily possible to have //chrome in data_deps without changing
+    # the //chrome target to bundle up both initial/chrome.exe and chrome.exe.
+    # As a workaround, explicitly include a data dep on just chrome.exe, and
+    # add //chrome to deps above to make sure it's been built.
+    data = [
+      "$root_out_dir/chrome.exe",
+    ]
+
+    # Don't want the test-specific dependencies to affect load tests.
+    # In particular, a few system DLLs cause user32 to be loaded, which is bad.
+    ldflags = [
+      "/DELAYLOAD:advapi32.dll",
+      "/DELAYLOAD:ole32.dll",
+      "/DELAYLOAD:shell32.dll",
+      "/DELAYLOAD:shlwapi.dll",
+      "/DELAYLOAD:user32.dll",
+      "/DELAYLOAD:winmm.dll",
+    ]
+  }
+}
diff --git a/chrome/test/base/browser_tests_main.cc b/chrome/test/base/browser_tests_main.cc
index 3653698..c271eef 100644
--- a/chrome/test/base/browser_tests_main.cc
+++ b/chrome/test/base/browser_tests_main.cc
@@ -23,6 +23,13 @@
   }
 
 #if defined(OS_WIN)
+  // Many tests validate code that requires user32.dll to be loaded. Loading it,
+  // however, cannot be done on the main thread loop because it is a blocking
+  // call, and all the test code runs on the main thread loop. Instead, just
+  // load and pin the module early on in startup before the blocking becomes an
+  // issue.
+  base::win::PinUser32();
+
   // Enable high-DPI for interactive tests where the user is expected to
   // manually verify results.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chrome/test/delayload/delayloads_unittest.cc b/chrome/test/delayload/delayloads_unittest.cc
new file mode 100644
index 0000000..22bac73
--- /dev/null
+++ b/chrome/test/delayload/delayloads_unittest.cc
@@ -0,0 +1,319 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/path_service.h"
+#include "base/strings/pattern.h"
+#include "base/strings/string_util.h"
+#include "base/test/launcher/test_launcher.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#include "build/build_config.h"
+#include "chrome/install_static/test/scoped_install_details.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class DelayloadsTest : public testing::Test {
+ protected:
+  static bool ImportsCallback(const base::win::PEImage& image,
+                              LPCSTR module,
+                              PIMAGE_THUNK_DATA name_table,
+                              PIMAGE_THUNK_DATA iat,
+                              PVOID cookie) {
+    std::vector<std::string>* import_list =
+        reinterpret_cast<std::vector<std::string>*>(cookie);
+    import_list->push_back(module);
+    return true;
+  }
+
+  static void GetImports(const base::FilePath& module_path,
+                         std::vector<std::string>* imports) {
+    ASSERT_TRUE(imports != NULL);
+
+    base::MemoryMappedFile module_mmap;
+
+    ASSERT_TRUE(module_mmap.Initialize(module_path));
+    base::win::PEImageAsData pe_image_data(
+        reinterpret_cast<HMODULE>(const_cast<uint8_t*>(module_mmap.data())));
+    pe_image_data.EnumImportChunks(DelayloadsTest::ImportsCallback, imports);
+  }
+};
+
+// Run this test only in Release builds.
+//
+// These tests make sure that chrome.dll, chrome_child.dll, and chrome.exe
+// have only certain types of imports.
+// In particular, we explicitly want to ensure user32.dll and its many related
+// dlls are delayloaded and not automatically brought in via some other
+// dependent dll. The primary reason for this is that the sandbox for the
+// renderer process prevents user32 from working at all and therefore we have
+// no reason to load the dll.
+// However, they directly and indirectly depend on base, which has lots more
+// imports than are allowed here.
+//
+// In release builds, the offending imports are all stripped since this
+// depends on a relatively small portion of base.
+//
+// If you break these tests, you may have changed base or the Windows sandbox
+// such that more system imports are required to link.
+//
+// Also note that the dlls are listed with specific case-sensitive names. If
+// you fail a test double-check that casing of the name.
+#if defined(NDEBUG) && !defined(COMPONENT_BUILD)
+
+TEST_F(DelayloadsTest, ChromeDllDelayloadsCheck) {
+  base::FilePath dll;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
+  dll = dll.Append(L"chrome.dll");
+  std::vector<std::string> dll_imports;
+  GetImports(dll, &dll_imports);
+
+  // Check that the dll has imports.
+  ASSERT_LT(0u, dll_imports.size())
+      << "Ensure the delayloads_unittests "
+         "target was built, instead of delayloads_unittests.exe";
+
+  static const char* const kValidFilePatterns[] = {
+      "KERNEL32.dll",
+      "chrome_elf.dll",
+      "DWrite.dll",
+      "oneds.dll",
+      "telclient.dll",
+      // On 64 bit the Version API's like VerQueryValue come from VERSION.dll.
+      // It depends on kernel32, advapi32 and api-ms-win-crt*.dll. This should
+      // be ok.
+      "VERSION.dll",
+  };
+
+  // Make sure all of chrome.dll's imports are in the valid imports list.
+  for (const std::string& dll_import : dll_imports) {
+    bool match = false;
+    for (const char* kValidFilePattern : kValidFilePatterns) {
+      if (base::MatchPattern(dll_import, kValidFilePattern)) {
+        match = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(match) << "Illegal import in chrome.dll: " << dll_import;
+  }
+}
+
+TEST_F(DelayloadsTest, ChromeDllLoadSanityTest) {
+  // On Win7 we expect this test to result in user32.dll getting loaded. As a
+  // result, we need to ensure it is executed in its own test process. This
+  // "test" will re-launch with custom parameters to accomplish that.
+  base::CommandLine new_test =
+      base::CommandLine(base::CommandLine::ForCurrentProcess()->GetProgram());
+  new_test.AppendSwitchASCII(base::kGTestFilterFlag,
+                             "DelayloadsTest.DISABLED_ChromeDllLoadSanityTest");
+  new_test.AppendSwitch("gtest_also_run_disabled_tests");
+  new_test.AppendSwitch("single-process-tests");
+
+  std::string output;
+  ASSERT_TRUE(base::GetAppOutput(new_test, &output));
+  std::string crash_string =
+      "OK ] DelayloadsTest.DISABLED_ChromeDllLoadSanityTest";
+
+  if (output.find(crash_string) == std::string::npos) {
+    GTEST_FAIL() << "Couldn't find\n"
+                 << crash_string << "\n in output\n " << output;
+  }
+}
+
+// Note: This test is not actually disabled, it's just tagged disabled so that
+// the real run (above, in ChromeDllLoadSanityTest) can run it with an argument
+// added to the command line.
+TEST_F(DelayloadsTest, DISABLED_ChromeDllLoadSanityTest) {
+  base::FilePath dll;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
+  dll = dll.Append(L"chrome.dll");
+
+  // We don't expect user32 to be loaded in delayloads_unittests. If this
+  // test case fails, then it means that a dependency on user32 has crept into
+  // the delayloads_unittests executable, which needs to be removed.
+  // NOTE: it may be a secondary dependency of another system DLL.  If so,
+  // try adding a "/DELAYLOAD:<blah>.dll" to the build.gn file.
+  ASSERT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
+
+  HMODULE chrome_module_handle = ::LoadLibrary(dll.value().c_str());
+  ASSERT_TRUE(chrome_module_handle != nullptr);
+  // Loading chrome.dll should not load user32.dll on Win10.
+  // On Win7, chains of system dlls and lack of apisets result in it loading.
+  if (base::win::GetVersion() >= base::win::Version::WIN10) {
+    EXPECT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
+  } else {
+    EXPECT_NE(nullptr, ::GetModuleHandle(L"user32.dll"));
+  }
+  EXPECT_TRUE(!!::FreeLibrary(chrome_module_handle));
+}
+
+TEST_F(DelayloadsTest, ChromeChildDllDelayloadsCheck) {
+  base::FilePath dll;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
+  dll = dll.Append(L"chrome_child.dll");
+  std::vector<std::string> dll_imports;
+  GetImports(dll, &dll_imports);
+
+  // Check that the dll has imports.
+  ASSERT_LT(0u, dll_imports.size())
+      << "Ensure the delayloads_unittests "
+         "target was built, instead of delayloads_unittests.exe";
+
+  static const char* const kValidFilePatterns[] = {
+      "KERNEL32.dll",
+      "chrome_elf.dll",
+      "DWrite.dll",
+      "ADVAPI32.dll",
+      "CRYPT32.dll",
+      "dbghelp.dll",
+      "dhcpcsvc.DLL",
+      "IPHLPAPI.DLL",
+      "ntdll.dll",
+      "OLEAUT32.dll",
+      "Secur32.dll",
+      "UIAutomationCore.DLL",
+      "USERENV.dll",
+      "WINHTTP.dll",
+      "WINMM.dll",
+      "WINSPOOL.DRV",
+      "WINTRUST.dll",
+      "WS2_32.dll",
+      "WTSAPI32.dll",
+      // On 64 bit the Version API's like VerQueryValue come from VERSION.dll.
+      // It depends on kernel32, advapi32 and api-ms-win-crt*.dll. This should
+      // be ok.
+      "VERSION.dll",
+  };
+
+  // Make sure all of chrome_child.dll's imports are in the valid imports list.
+  for (const std::string& dll_import : dll_imports) {
+    bool match = false;
+    for (const char* kValidFilePattern : kValidFilePatterns) {
+      if (base::MatchPattern(dll_import, kValidFilePattern)) {
+        match = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(match) << "Illegal import in chrome_child.dll: " << dll_import;
+  }
+}
+
+TEST_F(DelayloadsTest, ChromeChildDllLoadSanityTest) {
+  // On Win7 we expect this test to result in user32.dll getting loaded. As a
+  // result, we need to ensure it is executed in its own test process. This
+  // "test" will re-launch with custom parameters to accomplish that.
+  base::CommandLine new_test =
+      base::CommandLine(base::CommandLine::ForCurrentProcess()->GetProgram());
+  new_test.AppendSwitchASCII(
+      base::kGTestFilterFlag,
+      "DelayloadsTest.DISABLED_ChromeChildDllLoadSanityTest");
+  new_test.AppendSwitch("gtest_also_run_disabled_tests");
+  new_test.AppendSwitch("single-process-tests");
+
+  std::string output;
+  ASSERT_TRUE(base::GetAppOutput(new_test, &output));
+  std::string crash_string =
+      "OK ] DelayloadsTest.DISABLED_ChromeChildDllLoadSanityTest";
+
+  if (output.find(crash_string) == std::string::npos) {
+    GTEST_FAIL() << "Couldn't find\n"
+                 << crash_string << "\n in output\n " << output;
+  }
+}
+
+// Note: This test is not actually disabled, it's just tagged disabled so that
+// the real run (above, in ChromeChildDllLoadSanityTest) can run it with an
+// argument added to the command line.
+TEST_F(DelayloadsTest, DISABLED_ChromeChildDllLoadSanityTest) {
+  base::FilePath dll;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
+  dll = dll.Append(L"chrome_child.dll");
+
+  // We don't expect user32 to be loaded in delayloads_unittests. If this
+  // test case fails, then it means that a dependency on user32 has crept into
+  // the delayloads_unittests executable, which needs to be removed.
+  // NOTE: it may be a secondary dependency of another system DLL.  If so,
+  // try adding a "/DELAYLOAD:<blah>.dll" to the build.gn file.
+  ASSERT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
+
+  HMODULE chrome_child_module_handle = ::LoadLibrary(dll.value().c_str());
+  ASSERT_TRUE(chrome_child_module_handle != nullptr);
+  // Loading chrome.dll should not load user32.dll on Win10.
+  // On Win7, chains of system dlls and lack of apisets result in it loading.
+  if (base::win::GetVersion() >= base::win::Version::WIN10) {
+    EXPECT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
+  } else {
+    EXPECT_NE(nullptr, ::GetModuleHandle(L"user32.dll"));
+  }
+  EXPECT_TRUE(!!::FreeLibrary(chrome_child_module_handle));
+}
+
+TEST_F(DelayloadsTest, ChromeExeDelayloadsCheck) {
+  std::vector<std::string> exe_imports;
+  base::FilePath exe;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe));
+  exe = exe.Append(L"chrome.exe");
+  GetImports(exe, &exe_imports);
+
+  // Check that chrome.exe has imports.
+  ASSERT_LT(0u, exe_imports.size())
+      << "Ensure the delayloads_unittests "
+         "target was built, instead of delayloads_unittests.exe";
+
+  static const char* const kValidFilePatterns[] = {
+      "KERNEL32.dll",
+      "chrome_elf.dll",
+      // On 64 bit the Version API's like VerQueryValue come from VERSION.dll.
+      // It depends on kernel32, advapi32 and api-ms-win-crt*.dll. This should
+      // be ok.
+      "VERSION.dll",
+  };
+
+  // Make sure all of chrome.exe's imports are in the valid imports list.
+  for (const std::string& exe_import : exe_imports) {
+    bool match = false;
+    for (const char* kValidFilePattern : kValidFilePatterns) {
+      if (base::MatchPattern(exe_import, kValidFilePattern)) {
+        match = true;
+        break;
+      }
+    }
+    EXPECT_TRUE(match) << "Illegal import in chrome.exe: " << exe_import;
+  }
+}
+
+#endif  // NDEBUG && !COMPONENT_BUILD
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  // Ensure that the CommandLine instance honors the command line passed in
+  // instead of the default behavior on Windows which is to use the shell32
+  // CommandLineToArgvW API. The delayloads_unittests test suite should
+  // not depend on user32 directly or indirectly (For the curious shell32
+  // depends on user32)
+  base::CommandLine::InitUsingArgvForTesting(argc, argv);
+
+  install_static::ScopedInstallDetails scoped_install_details;
+
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/chrome_elf/BUILD.gn b/chrome_elf/BUILD.gn
index 7643f279..b637b09 100644
--- a/chrome_elf/BUILD.gn
+++ b/chrome_elf/BUILD.gn
@@ -92,7 +92,13 @@
   ldflags = [
     "/DELAYLOAD:advapi32.dll",
     "/DELAYLOAD:dbghelp.dll",
+    "/DELAYLOAD:ole32.dll",
+    "/DELAYLOAD:oleaut32.dll",
+    "/DELAYLOAD:propsys.dll",
     "/DELAYLOAD:rpcrt4.dll",
+    "/DELAYLOAD:shell32.dll",
+    "/DELAYLOAD:shlwapi.dll",
+    "/DELAYLOAD:user32.dll",
     "/DELAYLOAD:winmm.dll",
   ]
   if (current_cpu == "x86") {
diff --git a/content/browser/renderer_host/direct_manipulation_helper_win.cc b/content/browser/renderer_host/direct_manipulation_helper_win.cc
index 4794b7ea..d986731 100644
--- a/content/browser/renderer_host/direct_manipulation_helper_win.cc
+++ b/content/browser/renderer_host/direct_manipulation_helper_win.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/win/window_event_target.h"
@@ -234,8 +235,8 @@
   using GetPointerTypeFn = BOOL(WINAPI*)(UINT32, POINTER_INPUT_TYPE*);
   UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
   POINTER_INPUT_TYPE pointer_type;
-  static GetPointerTypeFn get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
-      GetProcAddress(GetModuleHandleA("user32.dll"), "GetPointerType"));
+  static const auto get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
+      base::win::GetUser32FunctionPointer("GetPointerType"));
   if (get_pointer_type && get_pointer_type(pointer_id, &pointer_type) &&
       pointer_type == PT_TOUCHPAD) {
     HRESULT hr = viewport_->SetContact(pointer_id);
diff --git a/media/capture/video/win/video_capture_device_utils_win.cc b/media/capture/video/win/video_capture_device_utils_win.cc
index 07d4b1d..868ae19 100644
--- a/media/capture/video/win/video_capture_device_utils_win.cc
+++ b/media/capture/video/win/video_capture_device_utils_win.cc
@@ -6,6 +6,7 @@
 
 #include <iostream>
 
+#include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 
 namespace media {
@@ -65,9 +66,8 @@
 
 bool IsAutoRotationEnabled() {
   typedef BOOL(WINAPI * GetAutoRotationState)(PAR_STATE state);
-  GetAutoRotationState get_rotation_state =
-      reinterpret_cast<GetAutoRotationState>(::GetProcAddress(
-          GetModuleHandle(L"user32.dll"), "GetAutoRotationState"));
+  static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
+      base::win::GetUser32FunctionPointer("GetAutoRotationState"));
 
   if (get_rotation_state) {
     AR_STATE auto_rotation_state;
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 5be3ec72..4d93a3f 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2100,6 +2100,8 @@
       "urlmon.lib",
       "winhttp.lib",
     ]
+
+    ldflags = [ "/DELAYLOAD:urlmon.dll" ]
   }
 
   if (!is_nacl) {
diff --git a/services/service_manager/embedder/main.cc b/services/service_manager/embedder/main.cc
index 2f1c180..cd12986 100644
--- a/services/service_manager/embedder/main.cc
+++ b/services/service_manager/embedder/main.cc
@@ -46,6 +46,7 @@
 #include <windows.h>
 
 #include "base/win/process_startup_helper.h"
+#include "base/win/win_util.h"
 #include "ui/base/win/atl_module.h"
 #endif
 
@@ -132,9 +133,11 @@
   // HACK: Let Windows know that we have started.  This is needed to suppress
   // the IDC_APPSTARTING cursor from being displayed for a prolonged period
   // while a subprocess is starting.
-  PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
-  MSG msg;
-  PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+  if (base::win::IsUser32AndGdi32Available()) {
+    PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
+    MSG msg;
+    PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+  }
 #endif
 
 #if !defined(OFFICIAL_BUILD) && defined(OS_WIN)
diff --git a/third_party/openvr/BUILD.gn b/third_party/openvr/BUILD.gn
index b4f53a8..83ce412 100644
--- a/third_party/openvr/BUILD.gn
+++ b/third_party/openvr/BUILD.gn
@@ -71,4 +71,8 @@
     "src/src/vrcommon",
     "//third_party/jsoncpp/source/include",
   ]
+
+  if (is_win) {
+    ldflags = [ "/DELAYLOAD:shell32.dll" ]
+  }
 }
diff --git a/ui/base/ime/win/BUILD.gn b/ui/base/ime/win/BUILD.gn
index e241603..34333cf6 100644
--- a/ui/base/ime/win/BUILD.gn
+++ b/ui/base/ime/win/BUILD.gn
@@ -44,6 +44,8 @@
 
   libs = [ "imm32.lib" ]
 
+  ldflags = [ "/DELAYLOAD:imm32.dll" ]
+
   jumbo_excluded_sources = [
     # tsf_text_store.cc needs INITGUID to be defined before
     # including any header to properly generate GUID objects. That
diff --git a/ui/base/test/ui_controls_internal_win.cc b/ui/base/test/ui_controls_internal_win.cc
index fec214048..a577845 100644
--- a/ui/base/test/ui_controls_internal_win.cc
+++ b/ui/base/test/ui_controls_internal_win.cc
@@ -19,6 +19,7 @@
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/win/win_util.h"
 #include "ui/display/win/screen_win.h"
 #include "ui/events/keycodes/keyboard_code_conversion_win.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -632,18 +633,17 @@
   DCHECK_LE(num, kTouchesLengthCap);
 
   using InitializeTouchInjectionFn = BOOL(WINAPI*)(UINT32, DWORD);
-  static InitializeTouchInjectionFn initialize_touch_injection =
-      reinterpret_cast<InitializeTouchInjectionFn>(GetProcAddress(
-          GetModuleHandleA("user32.dll"), "InitializeTouchInjection"));
+  static const auto initialize_touch_injection =
+      reinterpret_cast<InitializeTouchInjectionFn>(
+          base::win::GetUser32FunctionPointer("InitializeTouchInjection"));
   if (!initialize_touch_injection ||
       !initialize_touch_injection(num, TOUCH_FEEDBACK_INDIRECT)) {
     return false;
   }
 
   using InjectTouchInputFn = BOOL(WINAPI*)(UINT32, POINTER_TOUCH_INFO*);
-  static InjectTouchInputFn inject_touch_input =
-      reinterpret_cast<InjectTouchInputFn>(
-          GetProcAddress(GetModuleHandleA("user32.dll"), "InjectTouchInput"));
+  static const auto inject_touch_input = reinterpret_cast<InjectTouchInputFn>(
+      base::win::GetUser32FunctionPointer("InjectTouchInput"));
   if (!inject_touch_input)
     return false;
 
diff --git a/ui/base/win/hwnd_subclass.cc b/ui/base/win/hwnd_subclass.cc
index 184698b..c6325d5 100644
--- a/ui/base/win/hwnd_subclass.cc
+++ b/ui/base/win/hwnd_subclass.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
 #include "base/stl_util.h"
+#include "ui/base/win/touch_input.h"
 #include "ui/gfx/win/hwnd_util.h"
 
 namespace {
@@ -34,19 +35,6 @@
   return reinterpret_cast<WNDPROC>(GetWindowLongPtr(target, GWLP_WNDPROC));
 }
 
-// Not defined before Win7
-BOOL GetTouchInputInfoWrapper(HTOUCHINPUT handle, UINT count,
-                              PTOUCHINPUT pointer, int size) {
-  typedef BOOL(WINAPI *GetTouchInputInfoPtr)(HTOUCHINPUT, UINT,
-                                             PTOUCHINPUT, int);
-  GetTouchInputInfoPtr get_touch_input_info_func =
-      reinterpret_cast<GetTouchInputInfoPtr>(
-          GetProcAddress(GetModuleHandleA("user32.dll"), "GetTouchInputInfo"));
-  if (get_touch_input_info_func)
-    return get_touch_input_info_func(handle, count, pointer, size);
-  return FALSE;
-}
-
 }  // namespace
 
 namespace ui {
@@ -140,8 +128,8 @@
   if (message == WM_TOUCH) {
     TOUCHINPUT point;
 
-    if (GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), 1,
-                                 &point, sizeof(TOUCHINPUT))) {
+    if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), 1,
+                                     &point, sizeof(TOUCHINPUT))) {
       POINT touch_location = {TOUCH_COORD_TO_PIXEL(point.x),
                               TOUCH_COORD_TO_PIXEL(point.y)};
       HWND actual_target = WindowFromPoint(touch_location);
diff --git a/ui/base/win/touch_input.cc b/ui/base/win/touch_input.cc
index 0114bdd..708d566 100644
--- a/ui/base/win/touch_input.cc
+++ b/ui/base/win/touch_input.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ui/base/win/touch_input.h"
+#include "base/win/win_util.h"
 
 namespace ui {
 
@@ -10,11 +11,9 @@
                               UINT count,
                               PTOUCHINPUT pointer,
                               int size) {
-  typedef BOOL(WINAPI *GetTouchInputInfoPtr)(HTOUCHINPUT, UINT,
-                                             PTOUCHINPUT, int);
-  static GetTouchInputInfoPtr get_touch_input_info_func =
-      reinterpret_cast<GetTouchInputInfoPtr>(
-          GetProcAddress(GetModuleHandleA("user32.dll"), "GetTouchInputInfo"));
+  static const auto get_touch_input_info_func =
+      reinterpret_cast<decltype(&::GetTouchInputInfo)>(
+          base::win::GetUser32FunctionPointer("GetTouchInputInfo"));
   if (get_touch_input_info_func)
     return get_touch_input_info_func(handle, count, pointer, size);
   return FALSE;
diff --git a/ui/display/win/screen_win.cc b/ui/display/win/screen_win.cc
index cf47db2..5b4fe65 100644
--- a/ui/display/win/screen_win.cc
+++ b/ui/display/win/screen_win.cc
@@ -330,16 +330,10 @@
 // static
 int ScreenWin::GetSystemMetricsForScaleFactor(float scale_factor, int metric) {
   if (base::win::IsProcessPerMonitorDpiAware()) {
-    static auto get_metric_for_dpi_func = []() {
-      using GetSystemMetricsForDpiPtr = decltype(::GetSystemMetricsForDpi)*;
-      HMODULE user32_dll = ::LoadLibrary(L"user32.dll");
-      if (user32_dll) {
-        return reinterpret_cast<GetSystemMetricsForDpiPtr>(
-            ::GetProcAddress(user32_dll, "GetSystemMetricsForDpi"));
-      }
-      return static_cast<GetSystemMetricsForDpiPtr>(nullptr);
-    }();
-
+    using GetSystemMetricsForDpiPtr = decltype(::GetSystemMetricsForDpi)*;
+    static const auto get_metric_for_dpi_func =
+        reinterpret_cast<GetSystemMetricsForDpiPtr>(
+            base::win::GetUser32FunctionPointer("GetSystemMetricsForDpi"));
     if (get_metric_for_dpi_func) {
       return get_metric_for_dpi_func(metric,
                                      GetDPIFromScalingFactor(scale_factor));
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 889ff886e..9e71a06 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -582,7 +582,12 @@
       "oleacc.lib",
       "uiautomationcore.lib",
     ]
-    ldflags = [ "/DELAYLOAD:user32.dll" ]
+    ldflags = [
+      "/DELAYLOAD:dwmapi.dll",
+      "/DELAYLOAD:imm32.dll",
+      "/DELAYLOAD:oleacc.dll",
+      "/DELAYLOAD:user32.dll",
+    ]
     deps += [
       "//third_party/iaccessible2",
       "//third_party/wtl",
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 9e686fc..5c80caa5 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -23,6 +23,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/scoped_gdi_object.h"
+#include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/accessibility/accessibility_switches.h"
@@ -422,18 +423,15 @@
   // Create the window.
   WindowImpl::Init(parent, bounds);
 
-  if (!called_enable_non_client_dpi_scaling_ &&
-      delegate_->HasFrame() &&
+  if (!called_enable_non_client_dpi_scaling_ && delegate_->HasFrame() &&
       base::win::IsProcessPerMonitorDpiAware()) {
-    static auto enable_child_window_dpi_message_func = []() {
-      // Derived signature; not available in headers.
-      // This call gets Windows to scale the non-client area when WM_DPICHANGED
-      // is fired.
-      using EnableChildWindowDpiMessagePtr = LRESULT (WINAPI*)(HWND, BOOL);
-      return reinterpret_cast<EnableChildWindowDpiMessagePtr>(
-                 GetProcAddress(GetModuleHandle(L"user32.dll"),
-                                "EnableChildWindowDpiMessage"));
-    }();
+    // Derived signature; not available in headers.
+    // This call gets Windows to scale the non-client area when
+    // WM_DPICHANGED is fired.
+    using EnableChildWindowDpiMessagePtr = LRESULT(WINAPI*)(HWND, BOOL);
+    static const auto enable_child_window_dpi_message_func =
+        reinterpret_cast<EnableChildWindowDpiMessagePtr>(
+            base::win::GetUser32FunctionPointer("EnableChildWindowDpiMessage"));
     if (enable_child_window_dpi_message_func)
       enable_child_window_dpi_message_func(hwnd(), TRUE);
   }
@@ -1920,11 +1918,12 @@
   using GetPointerTypeFn = BOOL(WINAPI*)(UINT32, POINTER_INPUT_TYPE*);
   UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
   POINTER_INPUT_TYPE pointer_type;
-  static GetPointerTypeFn get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
-      GetProcAddress(GetModuleHandleA("user32.dll"), "GetPointerType"));
+  static const auto get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
+      base::win::GetUser32FunctionPointer("GetPointerType"));
   if (get_pointer_type && get_pointer_type(pointer_id, &pointer_type) &&
-      pointer_type == PT_TOUCHPAD)
+      pointer_type == PT_TOUCHPAD) {
     return PA_NOACTIVATE;
+  }
   SetMsgHandled(FALSE);
   return -1;
 }
@@ -1941,8 +1940,8 @@
   UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
   using GetPointerTypeFn = BOOL(WINAPI*)(UINT32, POINTER_INPUT_TYPE*);
   POINTER_INPUT_TYPE pointer_type;
-  static GetPointerTypeFn get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
-      GetProcAddress(GetModuleHandleA("user32.dll"), "GetPointerType"));
+  static const auto get_pointer_type = reinterpret_cast<GetPointerTypeFn>(
+      base::win::GetUser32FunctionPointer("GetPointerType"));
   // If the WM_POINTER messages are not sent from a stylus device, then we do
   // not handle them to make sure we do not change the current behavior of
   // touch and mouse inputs.
@@ -1959,9 +1958,10 @@
         return HandlePointerEventTypeTouch(message, w_param, l_param);
       FALLTHROUGH;
     default:
-      SetMsgHandled(FALSE);
-      return -1;
+      break;
   }
+  SetMsgHandled(FALSE);
+  return -1;
 }
 
 void HWNDMessageHandler::OnMove(const gfx::Point& point) {
@@ -2130,11 +2130,10 @@
 LRESULT HWNDMessageHandler::OnNCCreate(LPCREATESTRUCT lpCreateStruct) {
   SetMsgHandled(FALSE);
   if (delegate_->HasFrame() && base::win::IsProcessPerMonitorDpiAware()) {
-    static auto enable_non_client_dpi_scaling_func = []() {
-      return reinterpret_cast<decltype(::EnableNonClientDpiScaling)*>(
-                 GetProcAddress(GetModuleHandle(L"user32.dll"),
-                                "EnableNonClientDpiScaling"));
-    }();
+    using EnableNonClientDpiScalingPtr = decltype(::EnableNonClientDpiScaling)*;
+    static const auto enable_non_client_dpi_scaling_func =
+        reinterpret_cast<EnableNonClientDpiScalingPtr>(
+            base::win::GetUser32FunctionPointer("EnableNonClientDpiScaling"));
     called_enable_non_client_dpi_scaling_ =
         !!(enable_non_client_dpi_scaling_func &&
            enable_non_client_dpi_scaling_func(hwnd()));
@@ -2960,9 +2959,9 @@
   UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
   using GetPointerTouchInfoFn = BOOL(WINAPI*)(UINT32, POINTER_TOUCH_INFO*);
   POINTER_TOUCH_INFO pointer_touch_info;
-  static GetPointerTouchInfoFn get_pointer_touch_info =
-      reinterpret_cast<GetPointerTouchInfoFn>(GetProcAddress(
-          GetModuleHandleA("user32.dll"), "GetPointerTouchInfo"));
+  static const auto get_pointer_touch_info =
+      reinterpret_cast<GetPointerTouchInfoFn>(
+          base::win::GetUser32FunctionPointer("GetPointerTouchInfo"));
   if (!get_pointer_touch_info ||
       !get_pointer_touch_info(pointer_id, &pointer_touch_info)) {
     SetMsgHandled(FALSE);
@@ -3060,9 +3059,9 @@
   UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
   using GetPointerPenInfoFn = BOOL(WINAPI*)(UINT32, POINTER_PEN_INFO*);
   POINTER_PEN_INFO pointer_pen_info;
-  static GetPointerPenInfoFn get_pointer_pen_info =
+  static const auto get_pointer_pen_info =
       reinterpret_cast<GetPointerPenInfoFn>(
-          GetProcAddress(GetModuleHandleA("user32.dll"), "GetPointerPenInfo"));
+          base::win::GetUser32FunctionPointer("GetPointerPenInfo"));
   if (!get_pointer_pen_info ||
       !get_pointer_pen_info(pointer_id, &pointer_pen_info)) {
     SetMsgHandled(FALSE);
@@ -3080,13 +3079,12 @@
   // window, so use the weak ptr to check if destruction occured or not.
   base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
   if (event) {
-    if (event->IsTouchEvent()) {
+    if (event->IsTouchEvent())
       delegate_->HandleTouchEvent(event->AsTouchEvent());
-    } else if (event->IsMouseEvent()) {
+    else if (event->IsMouseEvent())
       delegate_->HandleMouseEvent(event->AsMouseEvent());
-    } else {
+    else
       NOTREACHED();
-    }
     last_touch_or_pen_message_time_ = ::GetMessageTime();
   }