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(¤t_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