xref: /aosp_15_r20/external/cronet/base/native_library_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/native_library.h"
6 
7 #include <windows.h>
8 
9 #include "base/files/file_util.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/path_service.h"
12 #include "base/scoped_native_library.h"
13 #include "base/strings/strcat.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/scoped_blocking_call.h"
19 #include "base/threading/scoped_thread_priority.h"
20 
21 namespace base {
22 
23 namespace {
24 
LoadNativeLibraryHelper(const FilePath & library_path,NativeLibraryLoadError * error)25 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
26                                       NativeLibraryLoadError* error) {
27   // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
28   // must not be called from DllMain.
29   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
30 
31   // Mitigate the issues caused by loading DLLs on a background thread
32   // (see http://crbug/973868 for context). This temporarily boosts this
33   // thread's priority so that it doesn't get starved by higher priority threads
34   // while it holds the LoaderLock.
35   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
36 
37   HMODULE module_handle = nullptr;
38 
39   // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
40   // directory as the library may have dependencies on DLLs in this
41   // directory.
42   module_handle = ::LoadLibraryExW(
43       library_path.value().c_str(), nullptr,
44       LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
45   // If LoadLibraryExW succeeds, log this metric and return.
46   if (module_handle) {
47     return module_handle;
48   }
49   // GetLastError() needs to be called immediately after
50   // LoadLibraryExW call.
51   if (error) {
52     error->code = ::GetLastError();
53   }
54 
55   // If LoadLibraryExW API/flags are unavailable or API call fails, try
56   // LoadLibraryW API. From UMA, this fallback is necessary for many users.
57 
58   // Switch the current directory to the library directory as the library
59   // may have dependencies on DLLs in this directory.
60   bool restore_directory = false;
61   FilePath current_directory;
62   if (GetCurrentDirectory(&current_directory)) {
63     FilePath plugin_path = library_path.DirName();
64     if (!plugin_path.empty()) {
65       SetCurrentDirectory(plugin_path);
66       restore_directory = true;
67     }
68   }
69   module_handle = ::LoadLibraryW(library_path.value().c_str());
70 
71   // GetLastError() needs to be called immediately after LoadLibraryW call.
72   if (!module_handle && error) {
73     error->code = ::GetLastError();
74   }
75 
76   if (restore_directory)
77     SetCurrentDirectory(current_directory);
78 
79   return module_handle;
80 }
81 
LoadSystemLibraryHelper(const FilePath & library_path,NativeLibraryLoadError * error)82 NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
83                                       NativeLibraryLoadError* error) {
84   // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
85   // hence must not be called from Dllmain.
86   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
87   NativeLibrary module;
88   BOOL module_found =
89       ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
90   if (!module_found) {
91     module = ::LoadLibraryExW(library_path.value().c_str(), nullptr,
92                               LOAD_LIBRARY_SEARCH_SYSTEM32);
93 
94     if (!module && error)
95       error->code = ::GetLastError();
96   }
97 
98   return module;
99 }
100 
GetSystemLibraryName(FilePath::StringPieceType name)101 FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
102   FilePath library_path;
103   // Use an absolute path to load the DLL to avoid DLL preloading attacks.
104   if (PathService::Get(DIR_SYSTEM, &library_path))
105     library_path = library_path.Append(name);
106   return library_path;
107 }
108 
109 }  // namespace
110 
ToString() const111 std::string NativeLibraryLoadError::ToString() const {
112   return StringPrintf("%lu", code);
113 }
114 
LoadNativeLibraryWithOptions(const FilePath & library_path,const NativeLibraryOptions & options,NativeLibraryLoadError * error)115 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
116                                            const NativeLibraryOptions& options,
117                                            NativeLibraryLoadError* error) {
118   return LoadNativeLibraryHelper(library_path, error);
119 }
120 
UnloadNativeLibrary(NativeLibrary library)121 void UnloadNativeLibrary(NativeLibrary library) {
122   FreeLibrary(library);
123 }
124 
GetFunctionPointerFromNativeLibrary(NativeLibrary library,const char * name)125 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
126                                           const char* name) {
127   return reinterpret_cast<void*>(GetProcAddress(library, name));
128 }
129 
GetNativeLibraryName(StringPiece name)130 std::string GetNativeLibraryName(StringPiece name) {
131   DCHECK(IsStringASCII(name));
132   return StrCat({name, ".dll"});
133 }
134 
GetLoadableModuleName(StringPiece name)135 std::string GetLoadableModuleName(StringPiece name) {
136   return GetNativeLibraryName(name);
137 }
138 
LoadSystemLibrary(FilePath::StringPieceType name,NativeLibraryLoadError * error)139 NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
140                                 NativeLibraryLoadError* error) {
141   FilePath library_path = GetSystemLibraryName(name);
142   if (library_path.empty()) {
143     if (error)
144       error->code = ERROR_NOT_FOUND;
145     return nullptr;
146   }
147   return LoadSystemLibraryHelper(library_path, error);
148 }
149 
PinSystemLibrary(FilePath::StringPieceType name,NativeLibraryLoadError * error)150 NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
151                                NativeLibraryLoadError* error) {
152   FilePath library_path = GetSystemLibraryName(name);
153   if (library_path.empty()) {
154     if (error)
155       error->code = ERROR_NOT_FOUND;
156     return nullptr;
157   }
158 
159   // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
160   // Dllmain.
161   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
162   ScopedNativeLibrary module;
163   if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
164                            library_path.value().c_str(),
165                            ScopedNativeLibrary::Receiver(module).get())) {
166     return module.release();
167   }
168 
169   // Load and pin the library since it wasn't already loaded.
170   module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
171   if (!module.is_valid())
172     return nullptr;
173 
174   ScopedNativeLibrary temp;
175   if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
176                            library_path.value().c_str(),
177                            ScopedNativeLibrary::Receiver(temp).get())) {
178     return module.release();
179   }
180 
181   if (error)
182     error->code = ::GetLastError();
183   // Return nullptr since we failed to pin the module.
184   return nullptr;
185 }
186 
187 }  // namespace base
188