// Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "aemu/base/SharedLibrary.h" #include #include #include #include #include #include "aemu/base/files/PathUtils.h" #include "host-common/logging.h" #ifndef _WIN32 #include #include #endif using android::base::PathUtils; namespace android { namespace base { class LibrarySearchPaths { public: LibrarySearchPaths() = default; void addPath(const char* path) { mPaths.push_back(path); } void forEachPath(std::function func) { for (const auto& path: mPaths) { func(path); } } private: std::vector mPaths; }; LibrarySearchPaths* sSearchPaths() { static LibrarySearchPaths* paths = new LibrarySearchPaths; return paths; } static SharedLibrary::LibraryMap s_libraryMap; // static SharedLibrary* SharedLibrary::open(const char* libraryName) { INFO("SharedLibrary::open for [%s]", libraryName); char error[1]; return open(libraryName, error, sizeof(error)); } SharedLibrary* SharedLibrary::open(const char* libraryName, char* error, size_t errorSize) { auto lib = s_libraryMap.find(libraryName); if (lib == s_libraryMap.end()) { INFO("SharedLibrary::open for [%s]: not found in map, open for the first time", libraryName); SharedLibrary* load = do_open(libraryName, error, errorSize); if (load != nullptr) { s_libraryMap[libraryName] = std::unique_ptr(load); } return load; } return lib->second.get(); } #ifdef _WIN32 // static SharedLibrary* SharedLibrary::do_open(const char* libraryName, char* error, size_t errorSize) { INFO("SharedLibrary::open for [%s] (win32): call LoadLibrary", libraryName); HMODULE lib = LoadLibraryA(libraryName); // Try a bit harder to find the shared library if we cannot find it. if (!lib) { INFO("SharedLibrary::open for [%s] can't find in default path. Searching alternatives...", libraryName); sSearchPaths()->forEachPath([&lib, libraryName](const std::string& path) { if (!lib) { auto libName = PathUtils::join(path, libraryName); INFO("SharedLibrary::open for [%s]: trying [%s]", libraryName, libName.c_str()); lib = LoadLibraryA(libName.c_str()); INFO("SharedLibrary::open for [%s]: trying [%s]. found? %d", libraryName, libName.c_str(), lib != nullptr); } }); } if (lib) { constexpr size_t kMaxPathLength = 2048; char fullPath[kMaxPathLength]; GetModuleFileNameA(lib, fullPath, kMaxPathLength); INFO("SharedLibrary::open succeeded for [%s]. File name: [%s]", libraryName, fullPath); return new SharedLibrary(lib); } if (errorSize == 0) { INFO("SharedLibrary::open for [%s] failed, but no error", libraryName); return NULL; } // Convert error into human-readable message. DWORD errorCode = ::GetLastError(); LPSTR message = NULL; size_t messageLen = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &message, 0, NULL); int ret = snprintf(error, errorSize, "%.*s", (int)messageLen, message); if (ret < 0 || ret == static_cast(errorSize)) { // snprintf() on Windows doesn't behave as expected by C99, // this path is to ensure that the result is always properly // zero-terminated. ret = static_cast(errorSize - 1); error[ret] = '\0'; } // Remove any trailing \r\n added by FormatMessage if (ret > 0 && error[ret - 1] == '\n') { error[--ret] = '\0'; } if (ret > 0 && error[ret - 1] == '\r') { error[--ret] = '\0'; } INFO("Failed to load [%s]. Error string: [%s]", libraryName, error); return NULL; } SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {} SharedLibrary::~SharedLibrary() { if (mLib) { // BUG: 66013149 // In windows it sometimes hang on exit when destroying s_libraryMap. // Let's skip freeing the library, since pretty much the only situation // we need to do it, is on exit. //FreeLibrary(mLib); } } SharedLibrary::FunctionPtr SharedLibrary::findSymbol( const char* symbolName) const { if (!mLib || !symbolName) { return NULL; } return reinterpret_cast( GetProcAddress(mLib, symbolName)); } #else // !_WIN32 // static SharedLibrary* SharedLibrary::do_open(const char* libraryName, char* error, size_t errorSize) { INFO("SharedLibrary::open for [%s] (posix): begin", libraryName); const char* libPath = libraryName; char* path = NULL; const char* libBaseName = strrchr(libraryName, '/'); if (!libBaseName) { libBaseName = libraryName; } if (!strchr(libBaseName, '.')) { // There is no extension in this library name, so append one. #ifdef __APPLE__ static const char kDllExtension[] = ".dylib"; #else static const char kDllExtension[] = ".so"; #endif size_t pathLen = strlen(libraryName) + sizeof(kDllExtension); path = static_cast(malloc(pathLen)); snprintf(path, pathLen, "%s%s", libraryName, kDllExtension); libPath = path; } dlerror(); // clear error. #ifdef __APPLE__ // On OSX, some libraries don't include an extension (notably OpenGL) // On OSX we try to open |libraryName| first. If that doesn't exist, // we try |libraryName|.dylib INFO("SharedLibrary::open for [%s] (posix,darwin): call dlopen", libraryName); void* lib = dlopen(libraryName, RTLD_NOW); if (lib == NULL) { INFO( "SharedLibrary::open for [%s] (posix,darwin): failed, " "try again with [%s]", libraryName, libPath); lib = dlopen(libPath, RTLD_NOW); sSearchPaths()->forEachPath([&lib, libraryName, libPath](const std::string& path) { if (!lib) { auto libName = PathUtils::join(path, libraryName); INFO( "SharedLibrary::open for [%s] (posix,darwin): still failed, " "try [%s]", libraryName, libName.c_str()); lib = dlopen(libName.c_str(), RTLD_NOW); if (!lib) { auto libPathName = PathUtils::join(path, libPath); INFO( "SharedLibrary::open for [%s] (posix,darwin): still failed, " "try [%s]", libraryName, libPathName.c_str()); lib = dlopen(libPathName.c_str(), RTLD_NOW); } } }); } #else INFO("SharedLibrary::open for [%s] (posix,linux): call dlopen on [%s]", libraryName, libPath); void* lib = dlopen(libPath, RTLD_NOW); #endif sSearchPaths()->forEachPath([&lib, libPath, libraryName](const std::string& path) { if (!lib) { auto libPathName = PathUtils::join(path, libPath); INFO("SharedLibrary::open for [%s] (posix): try again with %s", libraryName, libPathName.c_str()); lib = dlopen(libPathName.c_str(), RTLD_NOW); } }); if (path) { free(path); } if (lib) { INFO("SharedLibrary::open succeeded for [%s].", libraryName); return new SharedLibrary(lib); } snprintf(error, errorSize, "%s", dlerror()); INFO("SharedLibrary::open for [%s] failed (posix). dlerror: [%s]", libraryName, error); return NULL; } SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {} SharedLibrary::~SharedLibrary() { if (mLib) { dlclose(mLib); } } SharedLibrary::FunctionPtr SharedLibrary::findSymbol( const char* symbolName) const { if (!mLib || !symbolName) { return NULL; } return reinterpret_cast(dlsym(mLib, symbolName)); } #endif // !_WIN32 // static void SharedLibrary::addLibrarySearchPath(const char* path) { sSearchPaths()->addPath(path); } } // namespace base } // namespace android