| // Copyright 2011 The Chromium Authors |
| // 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<string_view> |
| |
| #include"base/files/file_util.h" |
| #include"base/metrics/histogram_macros.h" |
| #include"base/path_service.h" |
| #include"base/scoped_native_library.h" |
| #include"base/strings/strcat.h" |
| #include"base/strings/string_util.h" |
| #include"base/strings/stringprintf.h" |
| #include"base/strings/utf_string_conversions.h" |
| #include"base/threading/scoped_blocking_call.h" |
| #include"base/threading/scoped_thread_priority.h" |
| |
| namespace base{ |
| |
| namespace{ |
| |
| NativeLibraryLoadNativeLibraryHelper(constFilePath& library_path, |
| NativeLibraryLoadError* error){ |
| // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence |
| // must not be called from DllMain. |
| ScopedBlockingCall scoped_blocking_call(FROM_HERE,BlockingType::MAY_BLOCK); |
| |
| // Mitigate the issues caused by loading DLLs on a background thread |
| // (see http://crbug/973868 for context). This temporarily boosts this |
| // thread's priority so that it doesn't get starved by higher priority threads |
| // while it holds the LoaderLock. |
| SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY(); |
| |
| HMODULE module_handle=nullptr; |
| |
| // 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_handle=::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_handle){ |
| return module_handle; |
| } |
| // 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. From UMA, this fallback is necessary for many users. |
| |
| // 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_handle=::LoadLibraryW(library_path.value().c_str()); |
| |
| // GetLastError() needs to be called immediately after LoadLibraryW call. |
| if(!module_handle&& error){ |
| error->code=::GetLastError(); |
| } |
| |
| if(restore_directory){ |
| SetCurrentDirectory(current_directory); |
| } |
| |
| return module_handle; |
| } |
| |
| NativeLibraryLoadSystemLibraryHelper(constFilePath& library_path, |
| NativeLibraryLoadError* error){ |
| // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock, |
| // hence must not be called from Dllmain. |
| ScopedBlockingCall scoped_blocking_call(FROM_HERE,BlockingType::MAY_BLOCK); |
| NativeLibrary module; |
| BOOL module_found= |
| ::GetModuleHandleExW(0, library_path.value().c_str(),&module); |
| if(!module_found){ |
| module=::LoadLibraryExW(library_path.value().c_str(),nullptr, |
| LOAD_LIBRARY_SEARCH_SYSTEM32); |
| |
| if(!module&& error){ |
| error->code=::GetLastError(); |
| } |
| } |
| |
| return module; |
| } |
| |
| FilePathGetSystemLibraryName(FilePath::StringViewType name){ |
| FilePath library_path; |
| // Use an absolute path to load the DLL to avoid DLL preloading attacks. |
| if(PathService::Get(DIR_SYSTEM,&library_path)){ |
| library_path= library_path.Append(name); |
| } |
| return library_path; |
| } |
| |
| }// namespace |
| |
| std::stringNativeLibraryLoadError::ToString()const{ |
| returnStringPrintf("%lu", code); |
| } |
| |
| NativeLibraryLoadNativeLibraryWithOptions(constFilePath& library_path, |
| constNativeLibraryOptions& options, |
| NativeLibraryLoadError* error){ |
| returnLoadNativeLibraryHelper(library_path, error); |
| } |
| |
| voidUnloadNativeLibrary(NativeLibrary library){ |
| FreeLibrary(library); |
| } |
| |
| void*GetFunctionPointerFromNativeLibrary(NativeLibrary library, |
| constchar* name){ |
| returnreinterpret_cast<void*>(GetProcAddress(library, name)); |
| } |
| |
| std::stringGetNativeLibraryName(std::string_view name){ |
| DCHECK(IsStringASCII(name)); |
| returnStrCat({name,".dll"}); |
| } |
| |
| std::stringGetLoadableModuleName(std::string_view name){ |
| returnGetNativeLibraryName(name); |
| } |
| |
| NativeLibraryLoadSystemLibrary(FilePath::StringViewType name, |
| NativeLibraryLoadError* error){ |
| FilePath library_path=GetSystemLibraryName(name); |
| if(library_path.empty()){ |
| if(error){ |
| error->code= ERROR_NOT_FOUND; |
| } |
| returnnullptr; |
| } |
| returnLoadSystemLibraryHelper(library_path, error); |
| } |
| |
| NativeLibraryPinSystemLibrary(FilePath::StringViewType name, |
| NativeLibraryLoadError* error){ |
| FilePath library_path=GetSystemLibraryName(name); |
| if(library_path.empty()){ |
| if(error){ |
| error->code= ERROR_NOT_FOUND; |
| } |
| returnnullptr; |
| } |
| |
| // GetModuleHandleEx acquires the LoaderLock, hence must not be called from |
| // Dllmain. |
| ScopedBlockingCall scoped_blocking_call(FROM_HERE,BlockingType::MAY_BLOCK); |
| ScopedNativeLibrary module; |
| if(::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, |
| library_path.value().c_str(), |
| ScopedNativeLibrary::Receiver(module).get())){ |
| return module.release(); |
| } |
| |
| // Load and pin the library since it wasn't already loaded. |
| module=ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error)); |
| if(!module.is_valid()){ |
| returnnullptr; |
| } |
| |
| ScopedNativeLibrary temp; |
| if(::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, |
| library_path.value().c_str(), |
| ScopedNativeLibrary::Receiver(temp).get())){ |
| return module.release(); |
| } |
| |
| if(error){ |
| error->code=::GetLastError(); |
| } |
| // Return nullptr since we failed to pin the module. |
| returnnullptr; |
| } |
| |
| }// namespace base |