OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/native_library.h" | 5 #include "base/native_library.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
| 10 #include "base/metrics/histogram_macros.h" |
10 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
11 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
12 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
13 #include "base/threading/thread_restrictions.h" | 14 #include "base/threading/thread_restrictions.h" |
14 | 15 |
15 namespace base { | 16 namespace base { |
16 | 17 |
17 typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name); | 18 using AddDllDirectory = HMODULE (*)(PCWSTR new_directory); |
18 | 19 |
19 namespace { | 20 namespace { |
| 21 // This enum is used to back an UMA histogram, and should therefore be treated |
| 22 // as append-only. |
| 23 enum 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. |
| 43 void 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. |
| 50 bool 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. |
| 66 LoadLibraryResult 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 } |
20 | 81 |
21 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path, | 82 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path, |
22 LoadLibraryFunction load_library_api, | |
23 NativeLibraryLoadError* error) { | 83 NativeLibraryLoadError* error) { |
24 // LoadLibrary() opens the file off disk. | 84 // LoadLibrary() opens the file off disk. |
25 ThreadRestrictions::AssertIOAllowed(); | 85 ThreadRestrictions::AssertIOAllowed(); |
26 | 86 |
| 87 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 |
| 112 // LoadLibraryW API. |
| 113 // TODO(chengx): Currently, if LoadLibraryExW API call fails, LoadLibraryW is |
| 114 // still tried. We should strictly prefer the LoadLibraryExW over the |
| 115 // LoadLibraryW if LoadLibraryW is statistically showing no extra benefits. If |
| 116 // UMA metric shows that FAIL_AND_FAIL is the primary failure mode and/or |
| 117 // FAIL_AND_SUCCESS is close to zero, we should remove this fallback. |
| 118 // (http://crbug.com/701944) |
| 119 |
27 // Switch the current directory to the library directory as the library | 120 // Switch the current directory to the library directory as the library |
28 // may have dependencies on DLLs in this directory. | 121 // may have dependencies on DLLs in this directory. |
29 bool restore_directory = false; | 122 bool restore_directory = false; |
30 FilePath current_directory; | 123 FilePath current_directory; |
31 if (GetCurrentDirectory(¤t_directory)) { | 124 if (GetCurrentDirectory(¤t_directory)) { |
32 FilePath plugin_path = library_path.DirName(); | 125 FilePath plugin_path = library_path.DirName(); |
33 if (!plugin_path.empty()) { | 126 if (!plugin_path.empty()) { |
34 SetCurrentDirectory(plugin_path); | 127 SetCurrentDirectory(plugin_path); |
35 restore_directory = true; | 128 restore_directory = true; |
36 } | 129 } |
37 } | 130 } |
38 | 131 |
39 HMODULE module = (*load_library_api)(library_path.value().c_str()); | 132 module = ::LoadLibraryW(library_path.value().c_str()); |
40 if (!module && error) { | 133 |
41 // GetLastError() needs to be called immediately after |load_library_api|. | 134 // GetLastError() needs to be called immediately after LoadLibraryW call. |
| 135 if (!module && error) |
42 error->code = GetLastError(); | 136 error->code = GetLastError(); |
43 } | |
44 | 137 |
45 if (restore_directory) | 138 if (restore_directory) |
46 SetCurrentDirectory(current_directory); | 139 SetCurrentDirectory(current_directory); |
47 | 140 |
| 141 // Get the library loading result and log it to UMA. |
| 142 LogLibrarayLoadResultToUMA( |
| 143 GetLoadLibraryResult(are_search_flags_available, !!module)); |
| 144 |
48 return module; | 145 return module; |
49 } | 146 } |
50 | |
51 } // namespace | 147 } // namespace |
52 | 148 |
53 std::string NativeLibraryLoadError::ToString() const { | 149 std::string NativeLibraryLoadError::ToString() const { |
54 return StringPrintf("%u", code); | 150 return StringPrintf("%u", code); |
55 } | 151 } |
56 | 152 |
57 // static | 153 // static |
58 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, | 154 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, |
59 const NativeLibraryOptions& options, | 155 const NativeLibraryOptions& options, |
60 NativeLibraryLoadError* error) { | 156 NativeLibraryLoadError* error) { |
61 return LoadNativeLibraryHelper(library_path, LoadLibraryW, error); | 157 return LoadNativeLibraryHelper(library_path, error); |
62 } | |
63 | |
64 NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) { | |
65 typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name); | |
66 | |
67 LoadLibraryFunction load_library = reinterpret_cast<LoadLibraryFunction>( | |
68 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW")); | |
69 | |
70 return LoadNativeLibraryHelper(library_path, load_library, NULL); | |
71 } | 158 } |
72 | 159 |
73 // static | 160 // static |
74 void UnloadNativeLibrary(NativeLibrary library) { | 161 void UnloadNativeLibrary(NativeLibrary library) { |
75 FreeLibrary(library); | 162 FreeLibrary(library); |
76 } | 163 } |
77 | 164 |
78 // static | 165 // static |
79 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, | 166 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, |
80 StringPiece name) { | 167 StringPiece name) { |
81 return GetProcAddress(library, name.data()); | 168 return GetProcAddress(library, name.data()); |
82 } | 169 } |
83 | 170 |
84 // static | 171 // static |
85 std::string GetNativeLibraryName(StringPiece name) { | 172 std::string GetNativeLibraryName(StringPiece name) { |
86 DCHECK(IsStringASCII(name)); | 173 DCHECK(IsStringASCII(name)); |
87 return name.as_string() + ".dll"; | 174 return name.as_string() + ".dll"; |
88 } | 175 } |
89 | 176 |
90 } // namespace base | 177 } // namespace base |
OLD | NEW |