| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/native_library.h" |
| |
| #include <windows.h> |
| |
| #include "base/files/file_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_restrictions.h" |
| |
| namespace base { |
| |
| using AddDllDirectory = HMODULE (*)(PCWSTR new_directory); |
| |
| namespace { |
| // This enum is used to back an UMA histogram, and should therefore be treated |
| // as append-only. |
| enum LoadLibraryResult { |
| // LoadLibraryExW API/flags are available and the call succeeds. |
| SUCCEED = 0, |
| // LoadLibraryExW API/flags are availabe to use but the call fails, then |
| // LoadLibraryW is used and succeeds. |
| FAIL_AND_SUCCEED, |
| // LoadLibraryExW API/flags are availabe to use but the call fails, then |
| // LoadLibraryW is used but fails as well. |
| FAIL_AND_FAIL, |
| // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used |
| // and succeeds. |
| UNAVAILABLE_AND_SUCCEED, |
| // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used |
| // but fails. |
| UNAVAILABLE_AND_FAIL, |
| // Add new items before this one, always keep this one at the end. |
| END |
| }; |
| |
| // A helper method to log library loading result to UMA. |
| void LogLibrarayLoadResultToUMA(LoadLibraryResult result) { |
| UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result, |
| LoadLibraryResult::END); |
| } |
| |
| // A helper method to check if AddDllDirectory method is available, thus |
| // LOAD_LIBRARY_SEARCH_* flags are available on systems. |
| bool AreSearchFlagsAvailable() { |
| // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have |
| // KB2533623 installed. To determine whether the flags are available, use |
| // GetProcAddress to get the address of the AddDllDirectory, |
| // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress |
| // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx. |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx |
| // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper |
| // method. |
| auto add_dll_dir_func = reinterpret_cast<AddDllDirectory>( |
| GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory")); |
| return !!add_dll_dir_func; |
| } |
| |
| // A helper method to encode the library loading result to enum |
| // LoadLibraryResult. |
| LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available, |
| bool has_load_library_succeeded) { |
| LoadLibraryResult result; |
| if (are_search_flags_available) { |
| if (has_load_library_succeeded) |
| result = LoadLibraryResult::FAIL_AND_SUCCEED; |
| else |
| result = LoadLibraryResult::FAIL_AND_FAIL; |
| } else if (has_load_library_succeeded) { |
| result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED; |
| } else { |
| result = LoadLibraryResult::UNAVAILABLE_AND_FAIL; |
| } |
| return result; |
| } |
| |
| NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path, |
| NativeLibraryLoadError* error) { |
| // LoadLibrary() opens the file off disk. |
| AssertBlockingAllowed(); |
| |
| HMODULE module = nullptr; |
| |
| // This variable records the library loading result. |
| LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED; |
| |
| bool are_search_flags_available = AreSearchFlagsAvailable(); |
| if (are_search_flags_available) { |
| // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library |
| // directory as the library may have dependencies on DLLs in this |
| // directory. |
| module = ::LoadLibraryExW( |
| library_path.value().c_str(), nullptr, |
| LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); |
| // If LoadLibraryExW succeeds, log this metric and return. |
| if (module) { |
| LogLibrarayLoadResultToUMA(load_library_result); |
| return module; |
| } |
| // GetLastError() needs to be called immediately after |
| // LoadLibraryExW call. |
| if (error) |
| error->code = GetLastError(); |
| } |
| |
| // If LoadLibraryExW API/flags are unavailable or API call fails, try |
| // LoadLibraryW API. |
| // TODO(chengx): Currently, if LoadLibraryExW API call fails, LoadLibraryW is |
| // still tried. We should strictly prefer the LoadLibraryExW over the |
| // LoadLibraryW if LoadLibraryW is statistically showing no extra benefits. If |
| // UMA metric shows that FAIL_AND_FAIL is the primary failure mode and/or |
| // FAIL_AND_SUCCESS is close to zero, we should remove this fallback. |
| // (http://crbug.com/701944) |
| |
| // Switch the current directory to the library directory as the library |
| // may have dependencies on DLLs in this directory. |
| bool restore_directory = false; |
| FilePath current_directory; |
| if (GetCurrentDirectory(¤t_directory)) { |
| FilePath plugin_path = library_path.DirName(); |
| if (!plugin_path.empty()) { |
| SetCurrentDirectory(plugin_path); |
| restore_directory = true; |
| } |
| } |
| |
| module = ::LoadLibraryW(library_path.value().c_str()); |
| |
| // GetLastError() needs to be called immediately after LoadLibraryW call. |
| if (!module && error) |
| error->code = GetLastError(); |
| |
| if (restore_directory) |
| SetCurrentDirectory(current_directory); |
| |
| // Get the library loading result and log it to UMA. |
| LogLibrarayLoadResultToUMA( |
| GetLoadLibraryResult(are_search_flags_available, !!module)); |
| |
| return module; |
| } |
| } // namespace |
| |
| std::string NativeLibraryLoadError::ToString() const { |
| return StringPrintf("%lu", code); |
| } |
| |
| NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, |
| const NativeLibraryOptions& options, |
| NativeLibraryLoadError* error) { |
| return LoadNativeLibraryHelper(library_path, error); |
| } |
| |
| void UnloadNativeLibrary(NativeLibrary library) { |
| FreeLibrary(library); |
| } |
| |
| void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, |
| StringPiece name) { |
| return reinterpret_cast<void*>(GetProcAddress(library, name.data())); |
| } |
| |
| std::string GetNativeLibraryName(StringPiece name) { |
| DCHECK(IsStringASCII(name)); |
| return name.as_string() + ".dll"; |
| } |
| |
| std::string GetLoadableModuleName(StringPiece name) { |
| return GetNativeLibraryName(name); |
| } |
| |
| } // namespace base |