Full implementation to override atexit and similar calls.
This commit is contained in:
parent
06ef061569
commit
0d15357a12
@ -274,17 +274,16 @@ namespace cling {
|
||||
llvm::StringRef code,
|
||||
bool withAccessControl);
|
||||
|
||||
///\brief Include C++ runtime headers and definitions.
|
||||
///\brief Initialize runtime and C/C++ level overrides
|
||||
///
|
||||
void IncludeCXXRuntime();
|
||||
|
||||
///\brief Include C runtime headers and definitions.
|
||||
///\param[in] NoRuntime - Don't include the runtime headers / gCling
|
||||
///\param[in] SyntaxOnly - In SyntaxOnly mode
|
||||
///\param[out] Globals - Global symbols that need to be emitted
|
||||
///
|
||||
void IncludeCRuntime();
|
||||
|
||||
///\brief Init atexit runtime delegation.
|
||||
///\returns The resulting Transation of initialization.
|
||||
///
|
||||
void InitAExit();
|
||||
Transaction* Initialize(bool NoRuntime, bool SyntaxOnly,
|
||||
llvm::SmallVectorImpl<llvm::StringRef>& Globals);
|
||||
|
||||
///\brief The target constructor to be called from both the delegating
|
||||
/// constructors. parentInterp might be nullptr.
|
||||
|
@ -38,8 +38,7 @@ namespace cling {
|
||||
|
||||
IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& diags,
|
||||
const clang::CompilerInstance& CI):
|
||||
m_externalIncrementalExecutor(nullptr),
|
||||
m_CurrentAtExitModule(0)
|
||||
m_externalIncrementalExecutor(nullptr)
|
||||
#if 0
|
||||
: m_Diags(diags)
|
||||
#endif
|
||||
@ -123,10 +122,11 @@ void IncrementalExecutor::shuttingDown() {
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementalExecutor::AddAtExitFunc(void (*func) (void*), void* arg) {
|
||||
void IncrementalExecutor::AddAtExitFunc(void (*func) (void*), void* arg,
|
||||
llvm::Module* M) {
|
||||
// Register a CXAAtExit function
|
||||
cling::internal::SpinLockGuard slg(m_AtExitFuncsSpinLock);
|
||||
m_AtExitFuncs.push_back(CXAAtExitElement(func, arg, m_CurrentAtExitModule));
|
||||
m_AtExitFuncs.push_back(CXAAtExitElement(func, arg, M));
|
||||
}
|
||||
|
||||
void unresolvedSymbol()
|
||||
@ -206,14 +206,6 @@ IncrementalExecutor::runStaticInitializersOnce(const Transaction& T) {
|
||||
llvm::Module* m = T.getModule();
|
||||
assert(m && "Module must not be null");
|
||||
|
||||
// Set m_CurrentAtExitModule to the Module, unset to 0 once done.
|
||||
struct AtExitModuleSetterRAII {
|
||||
llvm::Module*& m_AEM;
|
||||
AtExitModuleSetterRAII(llvm::Module* M, llvm::Module*& AEM): m_AEM(AEM)
|
||||
{ AEM = M; }
|
||||
~AtExitModuleSetterRAII() { m_AEM = 0; }
|
||||
} DSOHandleSetter(m, m_CurrentAtExitModule);
|
||||
|
||||
// We don't care whether something was unresolved before.
|
||||
m_unresolvedSymbols.clear();
|
||||
|
||||
@ -333,14 +325,15 @@ IncrementalExecutor::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
|
||||
}
|
||||
|
||||
bool
|
||||
IncrementalExecutor::addSymbol(const char* symbolName, void* symbolAddress) {
|
||||
return IncrementalJIT::searchLibraries(symbolName, symbolAddress).second;
|
||||
IncrementalExecutor::addSymbol(const char* Name, void* Addr,
|
||||
bool Jit) {
|
||||
return m_JIT->lookupSymbol(Name, Addr, Jit).second;
|
||||
}
|
||||
|
||||
void* IncrementalExecutor::getAddressOfGlobal(llvm::StringRef symbolName,
|
||||
bool* fromJIT /*=0*/) {
|
||||
// Return a symbol's address, and whether it was jitted.
|
||||
void* address = IncrementalJIT::searchLibraries(symbolName).first;
|
||||
void* address = m_JIT->lookupSymbol(symbolName).first;
|
||||
|
||||
// It's not from the JIT if it's in a dylib.
|
||||
if (fromJIT)
|
||||
|
@ -114,10 +114,6 @@ namespace cling {
|
||||
///
|
||||
AtExitFunctions m_AtExitFuncs;
|
||||
|
||||
///\brief Module for which registration of static destructors currently
|
||||
/// takes place.
|
||||
llvm::Module* m_CurrentAtExitModule;
|
||||
|
||||
///\brief Modules to emit upon the next call to the JIT.
|
||||
///
|
||||
std::vector<llvm::Module*> m_ModulesToJIT;
|
||||
@ -208,12 +204,13 @@ namespace cling {
|
||||
/// Allows runtime declaration of a function passing its pointer for being
|
||||
/// used by JIT generated code.
|
||||
///
|
||||
/// @param[in] symbolName - The name of the symbol as required by the
|
||||
/// @param[in] Name - The name of the symbol as required by the
|
||||
/// linker (mangled if needed)
|
||||
/// @param[in] symbolAddress - The function pointer to register
|
||||
/// @param[in] Address - The function pointer to register
|
||||
/// @param[in] JIT - Add to the JIT injected symbol table
|
||||
/// @returns true if the symbol is successfully registered, false otherwise.
|
||||
///
|
||||
bool addSymbol(const char* symbolName, void* symbolAddress);
|
||||
bool addSymbol(const char* Name, void* Address, bool JIT = false);
|
||||
|
||||
///\brief Add a llvm::Module to the JIT.
|
||||
///
|
||||
@ -251,7 +248,7 @@ namespace cling {
|
||||
|
||||
///\brief Keep track of the entities whose dtor we need to call.
|
||||
///
|
||||
void AddAtExitFunc(void (*func) (void*), void* arg);
|
||||
void AddAtExitFunc(void (*func) (void*), void* arg, llvm::Module* M);
|
||||
|
||||
///\brief Try to resolve a symbol through our LazyFunctionCreators;
|
||||
/// print an error message if that fails.
|
||||
|
@ -25,12 +25,6 @@
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
// Forward cxa_atexit for global d'tors.
|
||||
static int local_cxa_atexit(void (*func) (void*), void* arg, void* dso) {
|
||||
cling::IncrementalExecutor* exe = (cling::IncrementalExecutor*)dso;
|
||||
exe->AddAtExitFunc(func, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
///\brief Memory manager providing the lop-level link to the
|
||||
/// IncrementalExecutor, handles missing or special / replaced symbols.
|
||||
@ -195,17 +189,6 @@ IncrementalJIT::IncrementalJIT(IncrementalExecutor& exe,
|
||||
llvm::orc::JITSymbol
|
||||
IncrementalJIT::getInjectedSymbols(const std::string& Name) const {
|
||||
using JITSymbol = llvm::orc::JITSymbol;
|
||||
if (Name == MANGLE_PREFIX "__cxa_atexit") {
|
||||
// Rewire __cxa_atexit to ~Interpreter(), thus also global destruction
|
||||
// coming from the JIT.
|
||||
return JITSymbol((uint64_t)&local_cxa_atexit,
|
||||
llvm::JITSymbolFlags::Exported);
|
||||
} else if (Name == MANGLE_PREFIX "__dso_handle") {
|
||||
// Provide IncrementalExecutor as the third argument to __cxa_atexit.
|
||||
return JITSymbol((uint64_t)&m_Parent,
|
||||
llvm::JITSymbolFlags::Exported);
|
||||
}
|
||||
|
||||
auto SymMapI = m_SymbolMap.find(Name);
|
||||
if (SymMapI != m_SymbolMap.end())
|
||||
return JITSymbol(SymMapI->second, llvm::JITSymbolFlags::Exported);
|
||||
@ -214,7 +197,7 @@ IncrementalJIT::getInjectedSymbols(const std::string& Name) const {
|
||||
}
|
||||
|
||||
std::pair<void*, bool>
|
||||
IncrementalJIT::searchLibraries(llvm::StringRef Name, void *InAddr) {
|
||||
IncrementalJIT::lookupSymbol(llvm::StringRef Name, void *InAddr, bool Jit) {
|
||||
// FIXME: See comments on DLSym below.
|
||||
#if !defined(LLVM_ON_WIN32)
|
||||
void* Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(Name);
|
||||
@ -222,7 +205,12 @@ IncrementalJIT::searchLibraries(llvm::StringRef Name, void *InAddr) {
|
||||
void* Addr = const_cast<void*>(platform::DLSym(Name));
|
||||
#endif
|
||||
|
||||
if (InAddr && !Addr) {
|
||||
if (InAddr && (!Addr || Jit)) {
|
||||
if (Jit) {
|
||||
std::string Key(Name);
|
||||
Key.insert(0, MANGLE_PREFIX);
|
||||
m_SymbolMap[Key] = llvm::orc::TargetAddress(InAddr);
|
||||
}
|
||||
llvm::sys::DynamicLibrary::AddSymbol(Name, InAddr);
|
||||
return std::make_pair(InAddr, true);
|
||||
}
|
||||
|
@ -215,9 +215,10 @@ public:
|
||||
///\brief Get the address of a symbol from the process' loaded libraries.
|
||||
/// \param Name - symbol to look for
|
||||
/// \param Addr - known address of the symbol that can be cached later use
|
||||
/// \param Jit - add to the injected symbols cache
|
||||
/// \returns The address of the symbol and whether it was cached
|
||||
static std::pair<void*, bool>
|
||||
searchLibraries(llvm::StringRef Name, void* Addr = nullptr);
|
||||
std::pair<void*, bool>
|
||||
lookupSymbol(llvm::StringRef Name, void* Addr = nullptr, bool Jit = false);
|
||||
};
|
||||
} // end cling
|
||||
#endif // CLING_INCREMENTAL_EXECUTOR_H
|
||||
|
@ -57,6 +57,13 @@ using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
// Forward cxa_atexit for global d'tors.
|
||||
static int local_cxa_atexit(void (*func) (void*), void* arg,
|
||||
cling::Interpreter* Interp) {
|
||||
Interp->AddAtExitFunc(func, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cling::Interpreter::ExecutionResult
|
||||
ConvertExecutionResult(cling::IncrementalExecutor::ExecutionResult ExeRes) {
|
||||
switch (ExeRes) {
|
||||
@ -188,16 +195,30 @@ namespace cling {
|
||||
|
||||
handleFrontendOptions();
|
||||
|
||||
if (!noRuntime) {
|
||||
if (getCI()->getLangOpts().CPlusPlus)
|
||||
IncludeCXXRuntime();
|
||||
else
|
||||
IncludeCRuntime();
|
||||
}
|
||||
llvm::SmallVector<llvm::StringRef, 6> Syms;
|
||||
Initialize(noRuntime, isInSyntaxOnlyMode(), Syms);
|
||||
|
||||
// Commit the transactions, now that gCling is set up. It is needed for
|
||||
// static initialization in these transactions through local_cxa_atexit().
|
||||
for (auto&& I: IncrParserTransactions)
|
||||
m_IncrParser->commitTransaction(I);
|
||||
|
||||
// Now that the transactions have been commited, force symbol emission
|
||||
// and overrides.
|
||||
if (const Transaction* T = getLastTransaction()) {
|
||||
if (llvm::Module* M = T->getModule()) {
|
||||
for (const llvm::StringRef& Sym : Syms) {
|
||||
if (const llvm::GlobalValue* GV = M->getNamedValue(Sym)) {
|
||||
if (void* Addr = m_Executor->getPointerToGlobalFromJIT(*GV))
|
||||
m_Executor->addSymbol(Sym.str().c_str(), Addr, true);
|
||||
else
|
||||
cling::errs() << Sym << " not defined\n";
|
||||
} else
|
||||
cling::errs() << Sym << " not in Module!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disable suggestions for ROOT
|
||||
bool showSuggestions = !llvm::StringRef(ClingStringify(CLING_VERSION)).startswith("ROOT");
|
||||
|
||||
@ -216,8 +237,6 @@ namespace cling {
|
||||
// Prevents stripping the symbol due to dead-code optimization.
|
||||
internal::symbol_requester();
|
||||
}
|
||||
|
||||
InitAExit();
|
||||
}
|
||||
|
||||
///\brief Constructor for the child Interpreter.
|
||||
@ -278,63 +297,93 @@ namespace cling {
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::InitAExit() {
|
||||
if (isInSyntaxOnlyMode())
|
||||
return;
|
||||
|
||||
const char* Linkage = getCI()->getLangOpts().CPlusPlus ? "extern \"C\"" : "";
|
||||
llvm::SmallString<512> Buf;
|
||||
Transaction* Interpreter::Initialize(bool NoRuntime, bool SyntaxOnly,
|
||||
llvm::SmallVectorImpl<llvm::StringRef>& Globals) {
|
||||
llvm::SmallString<1024> Buf;
|
||||
llvm::raw_svector_ostream Strm(Buf);
|
||||
Strm << Linkage << " int __cxa_atexit(void (*f) (void*), void*, void*);\n"
|
||||
<< Linkage << " int atexit(void(*f)()) {"
|
||||
"return __cxa_atexit((void(*)(void*))f, nullptr, (void*)"
|
||||
<< m_Executor.get() << "); }\n";
|
||||
Transaction *T = nullptr;
|
||||
declare(Strm.str(), &T);
|
||||
if (llvm::Module* M = T ? T->getModule() : nullptr) {
|
||||
if (const llvm::GlobalValue* GV = M->getNamedValue("atexit"))
|
||||
m_Executor->getPointerToGlobalFromJIT(*GV);
|
||||
const clang::LangOptions& LangOpts = getCI()->getLangOpts();
|
||||
const void* thisP = static_cast<void*>(this);
|
||||
|
||||
// FIXME: gCling should be const so assignemnt is a compile time error.
|
||||
// Currently the name mangling is coming up wrong for the const version
|
||||
// (on OS X at least, so probably Linux too) and the JIT thinks the symbol
|
||||
// is undefined in a child Interpreter. And speaking of children, should
|
||||
// gCling actually be thisCling, so a child Interpreter can only access
|
||||
// itself? One could use a macro (simillar to __dso_handle) to block
|
||||
// assignemnt and get around the mangling issue.
|
||||
const char* Linkage = LangOpts.CPlusPlus ? "extern \"C\"" : "";
|
||||
if (!NoRuntime && !SyntaxOnly) {
|
||||
if (LangOpts.CPlusPlus) {
|
||||
Strm << "#include \"cling/Interpreter/RuntimeUniverse.h\"\n"
|
||||
"namespace cling { class Interpreter; namespace runtime { "
|
||||
"Interpreter* gCling=(Interpreter*)" << thisP << "; }}\n";
|
||||
} else {
|
||||
Strm << "#include \"cling/Interpreter/CValuePrinter.h\"\n"
|
||||
"void* gCling=(void*)" << thisP << ";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::IncludeCXXRuntime() {
|
||||
// Set up common declarations which are going to be available
|
||||
// only at runtime
|
||||
// Make sure that the universe won't be included to compile time by using
|
||||
// -D __CLING__ as CompilerInstance's arguments
|
||||
// Intercept all atexit calls, as the Interpreter and functions will be long
|
||||
// gone when the -native- versions invoke them.
|
||||
if (!SyntaxOnly) {
|
||||
// While __dso_handle is still overriden in the JIT below,
|
||||
// #define __dso_handle is used to mitigate the following problems:
|
||||
// 1. Type of __dso_handle is void* making assignemnt to it legal
|
||||
// 2. Making it void* const in cling would mean possible type mismatch
|
||||
// 3. Cannot override void* __dso_handle in child Interpreter
|
||||
// 4. On Unix where the symbol actually exists, __dso_handle will be
|
||||
// linked into the code before the JIT can say otherwise, so:
|
||||
// [cling] __dso_handle // codegened __dso_handle always printed
|
||||
// [cling] __cxa_atexit(f, 0, __dso_handle) // seg-fault
|
||||
// 5. Code that actually uses __dso_handle will fail as a declaration is
|
||||
// needed which is not possible with the macro.
|
||||
// 6. Assuming 4 is sorted out in user code, calling __cxa_atexit through
|
||||
// atexit below isn't linking to the __dso_handle symbol.
|
||||
|
||||
std::stringstream initializer;
|
||||
#ifdef _WIN32
|
||||
// We have to use the #defined __CLING__ on windows first.
|
||||
//FIXME: Find proper fix.
|
||||
initializer << "#ifdef __CLING__ \n#endif\n";
|
||||
Strm << "#define __dso_handle ((void*)" << thisP << ")\n";
|
||||
|
||||
// Use __cxa_atexit to intercept all of the following routines
|
||||
Strm << Linkage << " int __cxa_atexit(void (*f)(void*), void*, void*);\n";
|
||||
|
||||
// C atexit, std::atexit
|
||||
Strm << Linkage << " int atexit(void(*f)()) { "
|
||||
"return __cxa_atexit((void(*)(void*))f, nullptr, __dso_handle); }\n";
|
||||
Globals.push_back("atexit");
|
||||
|
||||
// C++ 11 at_quick_exit, std::at_quick_exit
|
||||
if (LangOpts.CPlusPlus && LangOpts.CPlusPlus11) {
|
||||
Strm << Linkage << " int at_quick_exit(void(*f)()) { "
|
||||
"return __cxa_atexit((void(*)(void*))f, nullptr, __dso_handle); }\n";
|
||||
Globals.push_back("at_quick_exit");
|
||||
}
|
||||
|
||||
#if defined(LLVM_ON_WIN32)
|
||||
// Windows specific: _onexit, _onexit_m, __dllonexit
|
||||
Strm << Linkage << " _onexit_t _onexit(_onexit_t f) { "
|
||||
"__cxa_atexit((void(*)(void*))f, nullptr, __dso_handle); return f;}\n";
|
||||
Globals.push_back("_onexit");
|
||||
Strm << Linkage << " _onexit_t __dllonexit(_onexit_t f, PVFV**, PVFV**) { "
|
||||
"__cxa_atexit((void(*)(void*))f, nullptr, __dso_handle); return f;}\n";
|
||||
Globals.push_back("__dllonexit");
|
||||
#ifdef _M_CEE
|
||||
Strm << Linkage << " _onexit_t_m _onexit_m(_onexit_t f) { "
|
||||
"__cxa_atexit((void(*)(void*))f, nullptr, __dso_handle); return f;}\n";
|
||||
Globals.push_back("_onexit_m");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
initializer << "#include \"cling/Interpreter/RuntimeUniverse.h\"\n";
|
||||
|
||||
if (!isInSyntaxOnlyMode()) {
|
||||
// Set up the gCling variable if it can be used
|
||||
initializer << "namespace cling {namespace runtime { "
|
||||
"cling::Interpreter *gCling=(cling::Interpreter*)"
|
||||
<< "0x" << std::hex << (uintptr_t)this << " ;} }";
|
||||
// Override the native symbols now, before anything can be emitted.
|
||||
m_Executor->addSymbol("__cxa_atexit", (void*)&local_cxa_atexit, true);
|
||||
// __dso_handle is inserted for the link phase, as macro is useless then
|
||||
m_Executor->addSymbol("__dso_handle", this, true);
|
||||
}
|
||||
declare(initializer.str());
|
||||
}
|
||||
|
||||
void Interpreter::IncludeCRuntime() {
|
||||
// Set up the gCling variable if it can be used
|
||||
std::stringstream initializer;
|
||||
initializer << "void* gCling=(void*)" << (uintptr_t)this << ';';
|
||||
declare(initializer.str());
|
||||
// declare("void setValueNoAlloc(void* vpI, void* vpSVR, void* vpQT);");
|
||||
// declare("void setValueNoAlloc(void* vpI, void* vpV, void* vpQT, float value);");
|
||||
// declare("void setValueNoAlloc(void* vpI, void* vpV, void* vpQT, double value);");
|
||||
// declare("void setValueNoAlloc(void* vpI, void* vpV, void* vpQT, long double value);");
|
||||
// declare("void setValueNoAlloc(void* vpI, void* vpV, void* vpQT, unsigned long long value);");
|
||||
// declare("void setValueNoAlloc(void* vpI, void* vpV, void* vpQT, const void* value);");
|
||||
// declare("void* setValueWithAlloc(void* vpI, void* vpV, void* vpQT);");
|
||||
if (m_Opts.Verbose())
|
||||
llvm::errs() << Strm.str();
|
||||
|
||||
declare("#include \"cling/Interpreter/CValuePrinter.h\"");
|
||||
Transaction *T;
|
||||
declare(Strm.str(), &T);
|
||||
return T;
|
||||
}
|
||||
|
||||
void Interpreter::AddIncludePaths(llvm::StringRef PathStr, const char* Delm) {
|
||||
@ -1248,7 +1297,15 @@ namespace cling {
|
||||
}
|
||||
|
||||
void Interpreter::AddAtExitFunc(void (*Func) (void*), void* Arg) {
|
||||
m_Executor->AddAtExitFunc(Func, Arg);
|
||||
const Transaction* T = getCurrentTransaction();
|
||||
// Should this be ROOT only?
|
||||
if (!T) {
|
||||
// IncrementalParser::commitTransaction will call
|
||||
// Interpreter::executeTransaction if transaction has no parent.
|
||||
T = getLastTransaction();
|
||||
}
|
||||
assert(T && "No Transaction for Interpreter::AddAtExitFunc");
|
||||
m_Executor->AddAtExitFunc(Func, Arg, T->getModule());
|
||||
}
|
||||
|
||||
void Interpreter::GenerateAutoloadingMap(llvm::StringRef inFile,
|
||||
|
Loading…
Reference in New Issue
Block a user