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.
5 #include "base/native_library.h"
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"
25 // This enum is used to back an UMA histogram, and should therefore be treated
27 enum LoadLibraryResult {
28 // LoadLibraryExW API/flags are available and the call succeeds.
30 // LoadLibraryExW API/flags are availabe to use but the call fails, then
31 // LoadLibraryW is used and succeeds.
33 // LoadLibraryExW API/flags are availabe to use but the call fails, then
34 // LoadLibraryW is used but fails as well.
36 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
37 // and succeeds. Pre-Win10-only.
38 UNAVAILABLE_AND_SUCCEED_OBSOLETE,
39 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
40 // but fails. Pre-Win10-only.
41 UNAVAILABLE_AND_FAIL_OBSOLETE,
42 // Add new items before this one, always keep this one at the end.
46 // A helper method to log library loading result to UMA.
47 void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
48 UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
49 LoadLibraryResult::END);
52 // A helper method to encode the library loading result to enum
54 LoadLibraryResult GetLoadLibraryResult(bool has_load_library_succeeded) {
55 return has_load_library_succeeded ? LoadLibraryResult::FAIL_AND_SUCCEED
56 : LoadLibraryResult::FAIL_AND_FAIL;
59 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
60 NativeLibraryLoadError* error) {
61 // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
62 // must not be called from DllMain.
63 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
65 // Mitigate the issues caused by loading DLLs on a background thread
66 // (see http://crbug/973868 for context). This temporarily boosts this
67 // thread's priority so that it doesn't get starved by higher priority threads
68 // while it holds the LoaderLock.
69 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
71 HMODULE module_handle = nullptr;
73 // This variable records the library loading result.
74 LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
76 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
77 // directory as the library may have dependencies on DLLs in this
79 module_handle = ::LoadLibraryExW(
80 library_path.value().c_str(), nullptr,
81 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
82 // If LoadLibraryExW succeeds, log this metric and return.
84 LogLibrarayLoadResultToUMA(load_library_result);
87 // GetLastError() needs to be called immediately after
88 // LoadLibraryExW call.
90 error->code = ::GetLastError();
93 // If LoadLibraryExW API/flags are unavailable or API call fails, try
94 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
96 // Switch the current directory to the library directory as the library
97 // may have dependencies on DLLs in this directory.
98 bool restore_directory = false;
99 FilePath current_directory;
100 if (GetCurrentDirectory(¤t_directory)) {
101 FilePath plugin_path = library_path.DirName();
102 if (!plugin_path.empty()) {
103 SetCurrentDirectory(plugin_path);
104 restore_directory = true;
107 module_handle = ::LoadLibraryW(library_path.value().c_str());
109 // GetLastError() needs to be called immediately after LoadLibraryW call.
110 if (!module_handle && error) {
111 error->code = ::GetLastError();
114 if (restore_directory)
115 SetCurrentDirectory(current_directory);
117 // Get the library loading result and log it to UMA.
118 LogLibrarayLoadResultToUMA(GetLoadLibraryResult(!!module_handle));
120 return module_handle;
123 NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
124 NativeLibraryLoadError* error) {
125 // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
126 // hence must not be called from Dllmain.
127 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
128 NativeLibrary module;
130 ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
132 module = ::LoadLibraryExW(library_path.value().c_str(), nullptr,
133 LOAD_LIBRARY_SEARCH_SYSTEM32);
135 if (!module && error)
136 error->code = ::GetLastError();
138 LogLibrarayLoadResultToUMA(GetLoadLibraryResult(!!module));
144 FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
145 FilePath library_path;
146 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
147 if (PathService::Get(DIR_SYSTEM, &library_path))
148 library_path = library_path.Append(name);
154 std::string NativeLibraryLoadError::ToString() const {
155 return StringPrintf("%lu", code);
158 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
159 const NativeLibraryOptions& options,
160 NativeLibraryLoadError* error) {
161 return LoadNativeLibraryHelper(library_path, error);
164 void UnloadNativeLibrary(NativeLibrary library) {
165 FreeLibrary(library);
168 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
170 return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
173 std::string GetNativeLibraryName(StringPiece name) {
174 DCHECK(IsStringASCII(name));
175 return StrCat({name, ".dll"});
178 std::string GetLoadableModuleName(StringPiece name) {
179 return GetNativeLibraryName(name);
182 NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
183 NativeLibraryLoadError* error) {
184 FilePath library_path = GetSystemLibraryName(name);
185 if (library_path.empty()) {
187 error->code = ERROR_NOT_FOUND;
190 return LoadSystemLibraryHelper(library_path, error);
193 NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
194 NativeLibraryLoadError* error) {
195 FilePath library_path = GetSystemLibraryName(name);
196 if (library_path.empty()) {
198 error->code = ERROR_NOT_FOUND;
202 // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
204 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
205 ScopedNativeLibrary module;
206 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
207 library_path.value().c_str(),
208 ScopedNativeLibrary::Receiver(module).get())) {
209 return module.release();
212 // Load and pin the library since it wasn't already loaded.
213 module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
214 if (!module.is_valid())
217 ScopedNativeLibrary temp;
218 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
219 library_path.value().c_str(),
220 ScopedNativeLibrary::Receiver(temp).get())) {
221 return module.release();
225 error->code = ::GetLastError();
226 // Return nullptr since we failed to pin the module.