blob: 8282efc3d537f5626dd6312352d9ea409c7293aa [file] [log] [blame]
[email protected]84479322011-04-18 22:06:221// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[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"
thestig02c965b2016-06-14 18:52:2311#include "base/strings/string_util.h"
[email protected]f4e911452014-03-20 06:07:2612#include "base/strings/stringprintf.h"
[email protected]a4ea1f12013-06-07 18:37:0713#include "base/strings/utf_string_conversions.h"
Etienne Pierre-Doray3879b052018-09-17 14:17:2214#include "base/threading/scoped_blocking_call.h"
[email protected]f38e25f2009-04-21 00:56:0715
16namespace base {
17
chengx5946c922017-03-16 05:49:2118using AddDllDirectory = HMODULE (*)(PCWSTR new_directory);
[email protected]3e246222010-11-19 23:33:1319
[email protected]0f998442014-03-25 01:59:0920namespace {
chengx5946c922017-03-16 05:49:2121// This enum is used to back an UMA histogram, and should therefore be treated
22// as append-only.
23enum LoadLibraryResult {
24 // LoadLibraryExW API/flags are available and the call succeeds.
25 SUCCEED = 0,
26 // LoadLibraryExW API/flags are availabe to use but the call fails, then
27 // LoadLibraryW is used and succeeds.
28 FAIL_AND_SUCCEED,
29 // LoadLibraryExW API/flags are availabe to use but the call fails, then
30 // LoadLibraryW is used but fails as well.
31 FAIL_AND_FAIL,
32 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
33 // and succeeds.
34 UNAVAILABLE_AND_SUCCEED,
35 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
36 // but fails.
37 UNAVAILABLE_AND_FAIL,
38 // Add new items before this one, always keep this one at the end.
39 END
40};
41
42// A helper method to log library loading result to UMA.
43void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
44 UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
45 LoadLibraryResult::END);
46}
47
48// A helper method to check if AddDllDirectory method is available, thus
49// LOAD_LIBRARY_SEARCH_* flags are available on systems.
50bool AreSearchFlagsAvailable() {
51 // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have
52 // KB2533623 installed. To determine whether the flags are available, use
53 // GetProcAddress to get the address of the AddDllDirectory,
54 // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress
55 // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx.
56 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
57 // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper
58 // method.
59 auto add_dll_dir_func = reinterpret_cast<AddDllDirectory>(
60 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory"));
61 return !!add_dll_dir_func;
62}
63
64// A helper method to encode the library loading result to enum
65// LoadLibraryResult.
66LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available,
67 bool has_load_library_succeeded) {
68 LoadLibraryResult result;
69 if (are_search_flags_available) {
70 if (has_load_library_succeeded)
71 result = LoadLibraryResult::FAIL_AND_SUCCEED;
72 else
73 result = LoadLibraryResult::FAIL_AND_FAIL;
74 } else if (has_load_library_succeeded) {
75 result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED;
76 } else {
77 result = LoadLibraryResult::UNAVAILABLE_AND_FAIL;
78 }
79 return result;
80}
[email protected]0f998442014-03-25 01:59:0981
[email protected]3e246222010-11-19 23:33:1382NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
[email protected]0f998442014-03-25 01:59:0983 NativeLibraryLoadError* error) {
[email protected]be130682010-11-12 21:53:1684 // LoadLibrary() opens the file off disk.
Etienne Pierre-Doray3879b052018-09-17 14:17:2285 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
[email protected]be130682010-11-12 21:53:1686
chengx5946c922017-03-16 05:49:2187 HMODULE module = nullptr;
88
89 // This variable records the library loading result.
90 LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
91
92 bool are_search_flags_available = AreSearchFlagsAvailable();
93 if (are_search_flags_available) {
94 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
95 // directory as the library may have dependencies on DLLs in this
96 // directory.
97 module = ::LoadLibraryExW(
98 library_path.value().c_str(), nullptr,
99 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
100 // If LoadLibraryExW succeeds, log this metric and return.
101 if (module) {
102 LogLibrarayLoadResultToUMA(load_library_result);
103 return module;
104 }
105 // GetLastError() needs to be called immediately after
106 // LoadLibraryExW call.
107 if (error)
108 error->code = GetLastError();
109 }
110
111 // If LoadLibraryExW API/flags are unavailable or API call fails, try
Xi Cheng2740c2c2018-11-20 22:25:22112 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
chengx5946c922017-03-16 05:49:21113
[email protected]f38e25f2009-04-21 00:56:07114 // Switch the current directory to the library directory as the library
115 // may have dependencies on DLLs in this directory.
116 bool restore_directory = false;
[email protected]188505282009-09-16 16:31:28117 FilePath current_directory;
[email protected]37b3c1992014-03-11 20:59:02118 if (GetCurrentDirectory(&current_directory)) {
[email protected]f38e25f2009-04-21 00:56:07119 FilePath plugin_path = library_path.DirName();
[email protected]188505282009-09-16 16:31:28120 if (!plugin_path.empty()) {
[email protected]37b3c1992014-03-11 20:59:02121 SetCurrentDirectory(plugin_path);
[email protected]f38e25f2009-04-21 00:56:07122 restore_directory = true;
123 }
124 }
chengx5946c922017-03-16 05:49:21125 module = ::LoadLibraryW(library_path.value().c_str());
126
127 // GetLastError() needs to be called immediately after LoadLibraryW call.
128 if (!module && error)
[email protected]0f998442014-03-25 01:59:09129 error->code = GetLastError();
[email protected]f4e911452014-03-20 06:07:26130
[email protected]f38e25f2009-04-21 00:56:07131 if (restore_directory)
[email protected]37b3c1992014-03-11 20:59:02132 SetCurrentDirectory(current_directory);
[email protected]f38e25f2009-04-21 00:56:07133
chengx5946c922017-03-16 05:49:21134 // Get the library loading result and log it to UMA.
135 LogLibrarayLoadResultToUMA(
136 GetLoadLibraryResult(are_search_flags_available, !!module));
137
[email protected]f38e25f2009-04-21 00:56:07138 return module;
139}
[email protected]0f998442014-03-25 01:59:09140} // namespace
141
142std::string NativeLibraryLoadError::ToString() const {
Bruce Dawson19175842017-08-02 17:00:45143 return StringPrintf("%lu", code);
[email protected]0f998442014-03-25 01:59:09144}
145
rockot596a0dd2016-08-26 00:57:51146NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
147 const NativeLibraryOptions& options,
148 NativeLibraryLoadError* error) {
chengx5946c922017-03-16 05:49:21149 return LoadNativeLibraryHelper(library_path, error);
[email protected]3e246222010-11-19 23:33:13150}
151
[email protected]f38e25f2009-04-21 00:56:07152void UnloadNativeLibrary(NativeLibrary library) {
153 FreeLibrary(library);
154}
155
[email protected]f38e25f2009-04-21 00:56:07156void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
thestige38fbd62016-06-10 21:54:40157 StringPiece name) {
Nico Weberb6499668a2018-03-09 12:55:03158 return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
[email protected]f38e25f2009-04-21 00:56:07159}
160
thestig02c965b2016-06-14 18:52:23161std::string GetNativeLibraryName(StringPiece name) {
162 DCHECK(IsStringASCII(name));
163 return name.as_string() + ".dll";
[email protected]108c2a12009-06-05 22:18:09164}
165
Xiaohan Wangd807ec32018-04-03 01:31:44166std::string GetLoadableModuleName(StringPiece name) {
167 return GetNativeLibraryName(name);
168}
169
[email protected]f38e25f2009-04-21 00:56:07170} // namespace base