024b16df11
AutoloadCallback: Implementing InterpreterCallbacks to attach the module to cling The main focus is on overriding LookupObject so that the information about lookup failures are obtained from clang. The type of the name is not taken into consideration for now. TagManager: To manage and lookup information from various sorts of tag files. Currently a TagManager object is owned by the callback system. This may change in future. Wrapper: As a base class for handling particular types of tagfiles. The TagManager maintains a container of Wrappers. CtagsFileWrapper: Implementing a wrapper for ctags. This class is responsible for generating a tagfile from a given path or list of files. It also performs lookups in the file generated by it. And a few simple file system utils to complement llvm::sys::fs and path utilities
380 lines
12 KiB
Plaintext
380 lines
12 KiB
Plaintext
//------------------------------------------------------------------------------
|
|
// CLING - the C++ LLVM-based InterpreterG :)
|
|
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
|
|
//
|
|
// This file is dual-licensed: you can choose to license it under the University
|
|
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
|
// LICENSE.TXT for details.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "cling/Interpreter/DynamicLibraryManager.h"
|
|
#include "cling/Interpreter/Interpreter.h"
|
|
#include "cling/Interpreter/InterpreterCallbacks.h"
|
|
#include "cling/Interpreter/InvocationOptions.h"
|
|
|
|
#include "llvm/Support/DynamicLibrary.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef WIN32
|
|
#include <Windows.h>
|
|
#include <shlobj.h>
|
|
#else
|
|
#include <limits.h> /* PATH_MAX */
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
namespace {
|
|
#if defined(LLVM_ON_UNIX)
|
|
static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
|
|
char* env_var = getenv("LD_LIBRARY_PATH");
|
|
#if __APPLE__
|
|
if (!env_var)
|
|
env_var = getenv("DYLD_LIBRARY_PATH");
|
|
if (!env_var)
|
|
env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
|
|
#endif
|
|
if (env_var != 0) {
|
|
static const char PathSeparator = ':';
|
|
const char* at = env_var;
|
|
const char* delim = strchr(at, PathSeparator);
|
|
while (delim != 0) {
|
|
std::string tmp(at, size_t(delim-at));
|
|
if (llvm::sys::fs::is_directory(tmp.c_str()))
|
|
Paths.push_back(tmp);
|
|
at = delim + 1;
|
|
delim = strchr(at, PathSeparator);
|
|
}
|
|
|
|
if (*at != 0)
|
|
if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
|
|
Paths.push_back(at);
|
|
}
|
|
#if defined(__APPLE__) || defined(__CYGWIN__)
|
|
Paths.push_back("/usr/local/lib/");
|
|
Paths.push_back("/usr/X11R6/lib/");
|
|
Paths.push_back("/usr/lib/");
|
|
Paths.push_back("/lib/");
|
|
|
|
Paths.push_back("/lib/x86_64-linux-gnu/");
|
|
Paths.push_back("/usr/local/lib64/");
|
|
Paths.push_back("/usr/lib64/");
|
|
Paths.push_back("/lib64/");
|
|
#else
|
|
static bool initialized = false;
|
|
static std::vector<std::string> SysPaths;
|
|
if (!initialized) {
|
|
// trick to get the system search path
|
|
std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
|
|
FILE *pf = popen(cmd.c_str (), "r");
|
|
std::string result = "";
|
|
std::string sys_path = "";
|
|
char buffer[128];
|
|
while (!feof(pf)) {
|
|
if (fgets(buffer, 128, pf) != NULL)
|
|
result += buffer;
|
|
}
|
|
pclose(pf);
|
|
std::size_t from
|
|
= result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
|
|
std::size_t to = result.find("(system search path)");
|
|
if (from != std::string::npos && to != std::string::npos) {
|
|
from += 12;
|
|
sys_path = result.substr(from, to-from);
|
|
sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace),
|
|
sys_path.end());
|
|
sys_path += ':';
|
|
}
|
|
static const char PathSeparator = ':';
|
|
const char* at = sys_path.c_str();
|
|
const char* delim = strchr(at, PathSeparator);
|
|
while (delim != 0) {
|
|
std::string tmp(at, size_t(delim-at));
|
|
if (llvm::sys::fs::is_directory(tmp.c_str()))
|
|
SysPaths.push_back(tmp);
|
|
at = delim + 1;
|
|
delim = strchr(at, PathSeparator);
|
|
}
|
|
initialized = true;
|
|
}
|
|
|
|
for (std::vector<std::string>::const_iterator I = SysPaths.begin(),
|
|
E = SysPaths.end(); I != E; ++I)
|
|
Paths.push_back((*I).c_str());
|
|
#endif
|
|
}
|
|
#elif defined(LLVM_ON_WIN32)
|
|
static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
|
|
char buff[MAX_PATH];
|
|
// Generic form of C:\Windows\System32
|
|
HRESULT res = SHGetFolderPathA(NULL,
|
|
CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
|
|
NULL,
|
|
SHGFP_TYPE_CURRENT,
|
|
buff);
|
|
if (res != S_OK) {
|
|
assert(0 && "Failed to get system directory");
|
|
return;
|
|
}
|
|
Paths.push_back(buff);
|
|
|
|
// Reset buff.
|
|
buff[0] = 0;
|
|
// Generic form of C:\Windows
|
|
res = SHGetFolderPathA(NULL,
|
|
CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
|
|
NULL,
|
|
SHGFP_TYPE_CURRENT,
|
|
buff);
|
|
if (res != S_OK) {
|
|
assert(0 && "Failed to get windows directory");
|
|
return;
|
|
}
|
|
Paths.push_back(buff);
|
|
}
|
|
#else
|
|
# error "Unsupported platform."
|
|
#endif
|
|
|
|
}
|
|
|
|
namespace cling {
|
|
DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts,
|
|
Interpreter& I)
|
|
: m_Opts(Opts), m_Interpreter(I) {
|
|
GetSystemLibraryPaths(m_SystemSearchPaths);
|
|
m_SystemSearchPaths.push_back(".");
|
|
}
|
|
|
|
DynamicLibraryManager::~DynamicLibraryManager() {}
|
|
|
|
static bool isSharedLib(llvm::StringRef LibName, bool* exists = 0) {
|
|
using namespace llvm::sys::fs;
|
|
file_magic Magic;
|
|
llvm::error_code Error = identify_magic(LibName, Magic);
|
|
bool onDisk = (Error == llvm::errc::success);
|
|
if (exists)
|
|
*exists = onDisk;
|
|
|
|
return onDisk &&
|
|
#ifdef __APPLE__
|
|
(Magic == file_magic::macho_fixed_virtual_memory_shared_lib
|
|
|| Magic == file_magic::macho_dynamically_linked_shared_lib
|
|
|| Magic == file_magic::macho_dynamically_linked_shared_lib_stub)
|
|
#elif defined(LLVM_ON_UNIX)
|
|
#ifdef __CYGWIN__
|
|
(Magic == file_magic::pecoff_executable)
|
|
#else
|
|
(Magic == file_magic::elf_shared_object)
|
|
#endif
|
|
#elif defined(LLVM_ON_WIN32)
|
|
(Magic == file_magic::pecoff_executable)
|
|
#else
|
|
# error "Unsupported platform."
|
|
#endif
|
|
;
|
|
}
|
|
|
|
std::string
|
|
DynamicLibraryManager::lookupLibInPaths(llvm::StringRef libStem) const {
|
|
llvm::SmallVector<std::string, 128>
|
|
Paths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
|
|
Paths.append(m_SystemSearchPaths.begin(), m_SystemSearchPaths.end());
|
|
|
|
for (llvm::SmallVectorImpl<std::string>::const_iterator
|
|
IPath = Paths.begin(), E = Paths.end();IPath != E; ++IPath) {
|
|
llvm::SmallString<512> ThisPath(*IPath); // FIXME: move alloc outside loop
|
|
llvm::sys::path::append(ThisPath, libStem);
|
|
bool exists;
|
|
if (isSharedLib(ThisPath.str(), &exists))
|
|
return ThisPath.str();
|
|
if (exists)
|
|
return "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string
|
|
DynamicLibraryManager::lookupLibMaybeAddExt(llvm::StringRef libStem) const {
|
|
using namespace llvm::sys;
|
|
|
|
std::string foundDyLib = lookupLibInPaths(libStem);
|
|
|
|
if (foundDyLib.empty()) {
|
|
// Add DyLib extension:
|
|
llvm::SmallString<512> filenameWithExt(libStem);
|
|
#if defined(LLVM_ON_UNIX)
|
|
#ifdef __APPLE__
|
|
llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
|
|
#endif
|
|
static const char* DyLibExt = ".so";
|
|
#elif defined(LLVM_ON_WIN32)
|
|
static const char* DyLibExt = ".dll";
|
|
#else
|
|
# error "Unsupported platform."
|
|
#endif
|
|
filenameWithExt += DyLibExt;
|
|
foundDyLib = lookupLibInPaths(filenameWithExt);
|
|
#ifdef __APPLE__
|
|
if (foundDyLib.empty()) {
|
|
filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
|
|
filenameWithExt += ".dylib";
|
|
FfgoundDyLib = lookupLibInPaths(filenameWithExt);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (foundDyLib.empty())
|
|
return "";
|
|
|
|
// get canonical path name and check if already loaded
|
|
#if defined(LLVM_ON_WIN32)
|
|
llvm::SmallString<_MAX_PATH> FullPath("");
|
|
char *res = _fullpath((char *)FullPath.data(), foundDyLib.c_str(), _MAX_PATH);
|
|
#else
|
|
llvm::SmallString<PATH_MAX+1> FullPath("");
|
|
char *res = realpath(foundDyLib.c_str(), (char *)FullPath.data());
|
|
#endif
|
|
if (res == 0) {
|
|
llvm::errs() << "cling::DyLibMan::lookupLibMaybeAddExt(): error getting "
|
|
"real (canonical) path of library " << foundDyLib << '\n';
|
|
return foundDyLib;
|
|
}
|
|
FullPath.set_size(strlen(res));
|
|
return FullPath.str();
|
|
}
|
|
|
|
static std::string normalizePath(llvm::StringRef path) {
|
|
// Make the path canonical if the file exists.
|
|
struct stat buffer;
|
|
if (stat(path.data(), &buffer) != 0)
|
|
return "";
|
|
#if defined(LLVM_ON_WIN32)
|
|
char buf[_MAX_PATH];
|
|
char *res = _fullpath(buf, path.data(), _MAX_PATH);
|
|
#else
|
|
char buf[PATH_MAX+1];
|
|
char *res = realpath(path.data(), buf);
|
|
#endif
|
|
if (res == 0) {
|
|
assert(0 && "Cannot normalize!?");
|
|
return "";
|
|
}
|
|
return res;
|
|
}
|
|
|
|
std::string
|
|
DynamicLibraryManager::lookupLibrary(llvm::StringRef libStem) const {
|
|
llvm::SmallString<128> Absolute(libStem);
|
|
llvm::sys::fs::make_absolute(Absolute);
|
|
bool isAbsolute = libStem == Absolute;
|
|
|
|
// If it is an absolute path, don't try iterate over the paths.
|
|
if (isAbsolute) {
|
|
if (isSharedLib(libStem))
|
|
return normalizePath(libStem);
|
|
else
|
|
return "";
|
|
}
|
|
|
|
std::string foundName = lookupLibMaybeAddExt(libStem);
|
|
if (foundName.empty() && !libStem.startswith("lib")) {
|
|
// try with "lib" prefix:
|
|
foundName = lookupLibMaybeAddExt("lib" + libStem.str());
|
|
}
|
|
|
|
if (isSharedLib(foundName))
|
|
return normalizePath(foundName);
|
|
return "";
|
|
}
|
|
|
|
DynamicLibraryManager::LoadLibResult
|
|
DynamicLibraryManager::loadLibrary(const std::string& libStem,
|
|
bool permanent) {
|
|
std::string canonicalLoadedLib = lookupLibrary(libStem);
|
|
if (canonicalLoadedLib.empty())
|
|
return kLoadLibNotFound;
|
|
|
|
if (m_LoadedLibraries.find(canonicalLoadedLib) != m_LoadedLibraries.end())
|
|
return kLoadLibAlreadyLoaded;
|
|
|
|
std::string errMsg;
|
|
// TODO: !permanent case
|
|
#if defined(LLVM_ON_WIN32)
|
|
HMODULE dyLibHandle = LoadLibraryEx(canonicalLoadedLib.c_str(), NULL,
|
|
DONT_RESOLVE_DLL_REFERENCES);
|
|
errMsg = "LoadLibraryEx: GetLastError() returned ";
|
|
errMsg += GetLastError();
|
|
#else
|
|
const void* dyLibHandle = dlopen(canonicalLoadedLib.c_str(),
|
|
RTLD_LAZY|RTLD_GLOBAL);
|
|
if (const char* DyLibError = dlerror()) {
|
|
errMsg = DyLibError;
|
|
}
|
|
#endif
|
|
if (!dyLibHandle) {
|
|
llvm::errs() << "cling::DyLibMan::loadLibrary(): " << errMsg << '\n';
|
|
return kLoadLibLoadError;
|
|
}
|
|
else if (InterpreterCallbacks* C = m_Interpreter.getCallbacks())
|
|
C->LibraryLoaded(dyLibHandle, canonicalLoadedLib);
|
|
|
|
std::pair<DyLibs::iterator, bool> insRes
|
|
= m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
|
|
canonicalLoadedLib));
|
|
if (!insRes.second)
|
|
return kLoadLibAlreadyLoaded;
|
|
m_LoadedLibraries.insert(canonicalLoadedLib);
|
|
return kLoadLibSuccess;
|
|
}
|
|
|
|
void DynamicLibraryManager::unloadLibrary(llvm::StringRef libStem) {
|
|
std::string canonicalLoadedLib = lookupLibrary(libStem);
|
|
if (!isLibraryLoaded(canonicalLoadedLib))
|
|
return;
|
|
|
|
DyLibHandle dyLibHandle = 0;
|
|
for (DyLibs::const_iterator I = m_DyLibs.begin(), E = m_DyLibs.end();
|
|
I != E; ++I) {
|
|
if (I->second == canonicalLoadedLib)
|
|
dyLibHandle = I->first;
|
|
}
|
|
|
|
std::string errMsg;
|
|
// TODO: !permanent case
|
|
#if defined(LLVM_ON_WIN32)
|
|
UnloadLibraryEx(dyLibHandle);
|
|
errMsg = "UnoadLibraryEx: GetLastError() returned ";
|
|
errMsg += GetLastError();
|
|
#else
|
|
dlclose(const_cast<void*>(dyLibHandle));
|
|
if (const char* DyLibError = dlerror()) {
|
|
errMsg = DyLibError;
|
|
}
|
|
#endif
|
|
if (InterpreterCallbacks* C = m_Interpreter.getCallbacks())
|
|
C->LibraryUnloaded(dyLibHandle, canonicalLoadedLib);
|
|
|
|
m_DyLibs.erase(dyLibHandle);
|
|
m_LoadedLibraries.erase(canonicalLoadedLib);
|
|
}
|
|
|
|
bool DynamicLibraryManager::isLibraryLoaded(llvm::StringRef fullPath) const {
|
|
std::string canonPath = normalizePath(fullPath);
|
|
if (m_LoadedLibraries.find(canonPath) != m_LoadedLibraries.end())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
|
|
llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
|
|
}
|
|
} // end namespace cling
|