blob: 3c9882f86643387deff258240d3085adc6ae1e8d [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2011 The Chromium Authors
[email protected]f38e25f2009-04-21 00:56:072// 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
[email protected]e3177dd52014-08-13 20:22:149#include "base/files/file_util.h"
chengx5946c922017-03-16 05:49:2110#include "base/metrics/histogram_macros.h"
Cliff Smolinskyf395bef2019-04-12 23:45:4411#include "base/path_service.h"
12#include "base/scoped_native_library.h"
Jan Wilken Dörrie5db50ac2021-02-15 11:43:1613#include "base/strings/strcat.h"
14#include "base/strings/string_piece.h"
thestig02c965b2016-06-14 18:52:2315#include "base/strings/string_util.h"
[email protected]f4e911452014-03-20 06:07:2616#include "base/strings/stringprintf.h"
[email protected]a4ea1f12013-06-07 18:37:0717#include "base/strings/utf_string_conversions.h"
Etienne Pierre-Doray3879b052018-09-17 14:17:2218#include "base/threading/scoped_blocking_call.h"
Anthony Vallee-Dubois3aafcf22022-02-10 16:52:0419#include "base/threading/scoped_thread_priority.h"
[email protected]f38e25f2009-04-21 00:56:0720
21namespace base {
22
[email protected]0f998442014-03-25 01:59:0923namespace {
Cliff Smolinskyf395bef2019-04-12 23:45:4424
chengx5946c922017-03-16 05:49:2125// This enum is used to back an UMA histogram, and should therefore be treated
26// as append-only.
27enum LoadLibraryResult {
28 // LoadLibraryExW API/flags are available and the call succeeds.
29 SUCCEED = 0,
30 // LoadLibraryExW API/flags are availabe to use but the call fails, then
31 // LoadLibraryW is used and succeeds.
32 FAIL_AND_SUCCEED,
33 // LoadLibraryExW API/flags are availabe to use but the call fails, then
34 // LoadLibraryW is used but fails as well.
35 FAIL_AND_FAIL,
36 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
David Bienvenue2791612023-02-03 15:56:4537 // and succeeds. Pre-Win10-only.
38 UNAVAILABLE_AND_SUCCEED_OBSOLETE,
chengx5946c922017-03-16 05:49:2139 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
David Bienvenue2791612023-02-03 15:56:4540 // but fails. Pre-Win10-only.
41 UNAVAILABLE_AND_FAIL_OBSOLETE,
chengx5946c922017-03-16 05:49:2142 // Add new items before this one, always keep this one at the end.
43 END
44};
45
46// A helper method to log library loading result to UMA.
47void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
48 UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
49 LoadLibraryResult::END);
50}
51
chengx5946c922017-03-16 05:49:2152// A helper method to encode the library loading result to enum
53// LoadLibraryResult.
David Bienvenue2791612023-02-03 15:56:4554LoadLibraryResult GetLoadLibraryResult(bool has_load_library_succeeded) {
55 return has_load_library_succeeded ? LoadLibraryResult::FAIL_AND_SUCCEED
56 : LoadLibraryResult::FAIL_AND_FAIL;
chengx5946c922017-03-16 05:49:2157}
[email protected]0f998442014-03-25 01:59:0958
[email protected]3e246222010-11-19 23:33:1359NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
[email protected]0f998442014-03-25 01:59:0960 NativeLibraryLoadError* error) {
Cliff Smolinskyf395bef2019-04-12 23:45:4461 // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
62 // must not be called from DllMain.
Etienne Bergeron436d42212019-02-26 17:15:1263 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
[email protected]be130682010-11-12 21:53:1664
Anthony Vallee-Dubois3aafcf22022-02-10 16:52:0465 // 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();
70
David Bienvenue2791612023-02-03 15:56:4571 HMODULE module_handle = nullptr;
chengx5946c922017-03-16 05:49:2172
73 // This variable records the library loading result.
74 LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
75
David Bienvenue2791612023-02-03 15:56:4576 // 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
78 // directory.
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.
83 if (module_handle) {
84 LogLibrarayLoadResultToUMA(load_library_result);
85 return module_handle;
86 }
87 // GetLastError() needs to be called immediately after
88 // LoadLibraryExW call.
89 if (error) {
90 error->code = ::GetLastError();
chengx5946c922017-03-16 05:49:2191 }
92
93 // If LoadLibraryExW API/flags are unavailable or API call fails, try
Xi Cheng2740c2c2018-11-20 22:25:2294 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
chengx5946c922017-03-16 05:49:2195
[email protected]f38e25f2009-04-21 00:56:0796 // 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;
[email protected]188505282009-09-16 16:31:2899 FilePath current_directory;
[email protected]37b3c1992014-03-11 20:59:02100 if (GetCurrentDirectory(&current_directory)) {
[email protected]f38e25f2009-04-21 00:56:07101 FilePath plugin_path = library_path.DirName();
[email protected]188505282009-09-16 16:31:28102 if (!plugin_path.empty()) {
[email protected]37b3c1992014-03-11 20:59:02103 SetCurrentDirectory(plugin_path);
[email protected]f38e25f2009-04-21 00:56:07104 restore_directory = true;
105 }
106 }
David Bienvenue2791612023-02-03 15:56:45107 module_handle = ::LoadLibraryW(library_path.value().c_str());
chengx5946c922017-03-16 05:49:21108
109 // GetLastError() needs to be called immediately after LoadLibraryW call.
David Bienvenue2791612023-02-03 15:56:45110 if (!module_handle && error) {
Cliff Smolinskyc5c52102019-05-03 20:51:54111 error->code = ::GetLastError();
David Bienvenue2791612023-02-03 15:56:45112 }
[email protected]f4e911452014-03-20 06:07:26113
[email protected]f38e25f2009-04-21 00:56:07114 if (restore_directory)
[email protected]37b3c1992014-03-11 20:59:02115 SetCurrentDirectory(current_directory);
[email protected]f38e25f2009-04-21 00:56:07116
chengx5946c922017-03-16 05:49:21117 // Get the library loading result and log it to UMA.
David Bienvenue2791612023-02-03 15:56:45118 LogLibrarayLoadResultToUMA(GetLoadLibraryResult(!!module_handle));
chengx5946c922017-03-16 05:49:21119
David Bienvenue2791612023-02-03 15:56:45120 return module_handle;
[email protected]f38e25f2009-04-21 00:56:07121}
Cliff Smolinskyf395bef2019-04-12 23:45:44122
123NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
124 NativeLibraryLoadError* error) {
Cliff Smolinskyc5c52102019-05-03 20:51:54125 // 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);
Cliff Smolinskyf395bef2019-04-12 23:45:44128 NativeLibrary module;
129 BOOL module_found =
Jan Wilken Dörrieb630aca2019-12-04 10:59:11130 ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
Cliff Smolinskyf395bef2019-04-12 23:45:44131 if (!module_found) {
David Bienvenue2791612023-02-03 15:56:45132 module = ::LoadLibraryExW(library_path.value().c_str(), nullptr,
133 LOAD_LIBRARY_SEARCH_SYSTEM32);
Cliff Smolinskyf395bef2019-04-12 23:45:44134
135 if (!module && error)
136 error->code = ::GetLastError();
137
David Bienvenue2791612023-02-03 15:56:45138 LogLibrarayLoadResultToUMA(GetLoadLibraryResult(!!module));
Cliff Smolinskyf395bef2019-04-12 23:45:44139 }
140
141 return module;
142}
143
Lei Zhang4c836692019-09-27 02:14:55144FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
Cliff Smolinskyf395bef2019-04-12 23:45:44145 FilePath library_path;
146 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
Lei Zhang4c836692019-09-27 02:14:55147 if (PathService::Get(DIR_SYSTEM, &library_path))
148 library_path = library_path.Append(name);
149 return library_path;
Cliff Smolinskyf395bef2019-04-12 23:45:44150}
151
[email protected]0f998442014-03-25 01:59:09152} // namespace
153
154std::string NativeLibraryLoadError::ToString() const {
Bruce Dawson19175842017-08-02 17:00:45155 return StringPrintf("%lu", code);
[email protected]0f998442014-03-25 01:59:09156}
157
rockot596a0dd2016-08-26 00:57:51158NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
159 const NativeLibraryOptions& options,
160 NativeLibraryLoadError* error) {
chengx5946c922017-03-16 05:49:21161 return LoadNativeLibraryHelper(library_path, error);
[email protected]3e246222010-11-19 23:33:13162}
163
[email protected]f38e25f2009-04-21 00:56:07164void UnloadNativeLibrary(NativeLibrary library) {
165 FreeLibrary(library);
166}
167
[email protected]f38e25f2009-04-21 00:56:07168void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
thestige38fbd62016-06-10 21:54:40169 StringPiece name) {
Nico Weberb6499668a2018-03-09 12:55:03170 return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
[email protected]f38e25f2009-04-21 00:56:07171}
172
thestig02c965b2016-06-14 18:52:23173std::string GetNativeLibraryName(StringPiece name) {
174 DCHECK(IsStringASCII(name));
Jan Wilken Dörrie5db50ac2021-02-15 11:43:16175 return StrCat({name, ".dll"});
[email protected]108c2a12009-06-05 22:18:09176}
177
Xiaohan Wangd807ec32018-04-03 01:31:44178std::string GetLoadableModuleName(StringPiece name) {
179 return GetNativeLibraryName(name);
180}
181
Cliff Smolinskyf395bef2019-04-12 23:45:44182NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
183 NativeLibraryLoadError* error) {
Lei Zhang4c836692019-09-27 02:14:55184 FilePath library_path = GetSystemLibraryName(name);
185 if (library_path.empty()) {
186 if (error)
187 error->code = ERROR_NOT_FOUND;
188 return nullptr;
189 }
190 return LoadSystemLibraryHelper(library_path, error);
Cliff Smolinskyf395bef2019-04-12 23:45:44191}
192
Cliff Smolinskyc5c52102019-05-03 20:51:54193NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
194 NativeLibraryLoadError* error) {
Lei Zhang4c836692019-09-27 02:14:55195 FilePath library_path = GetSystemLibraryName(name);
196 if (library_path.empty()) {
Cliff Smolinskyc5c52102019-05-03 20:51:54197 if (error)
198 error->code = ERROR_NOT_FOUND;
199 return nullptr;
200 }
201
202 // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
203 // Dllmain.
204 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
205 ScopedNativeLibrary module;
Lei Zhang4c836692019-09-27 02:14:55206 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
Jan Wilken Dörrieb630aca2019-12-04 10:59:11207 library_path.value().c_str(),
Lei Zhang4c836692019-09-27 02:14:55208 ScopedNativeLibrary::Receiver(module).get())) {
209 return module.release();
Cliff Smolinskyc5c52102019-05-03 20:51:54210 }
Lei Zhang4c836692019-09-27 02:14:55211
212 // Load and pin the library since it wasn't already loaded.
213 module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
214 if (!module.is_valid())
215 return nullptr;
216
217 ScopedNativeLibrary temp;
218 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
Jan Wilken Dörrieb630aca2019-12-04 10:59:11219 library_path.value().c_str(),
Lei Zhang4c836692019-09-27 02:14:55220 ScopedNativeLibrary::Receiver(temp).get())) {
221 return module.release();
222 }
223
224 if (error)
225 error->code = ::GetLastError();
226 // Return nullptr since we failed to pin the module.
227 return nullptr;
Cliff Smolinskyc5c52102019-05-03 20:51:54228}
229
[email protected]f38e25f2009-04-21 00:56:07230} // namespace base