Full implementation to override atexit and similar calls.

This commit is contained in:
Frederich Munch 2016-10-01 00:36:12 -04:00 committed by sftnight
parent 06ef061569
commit 0d15357a12
6 changed files with 144 additions and 109 deletions

View File

@ -274,17 +274,16 @@ namespace cling {
llvm::StringRef code, llvm::StringRef code,
bool withAccessControl); bool withAccessControl);
///\brief Include C++ runtime headers and definitions. ///\brief Initialize runtime and C/C++ level overrides
/// ///
void IncludeCXXRuntime(); ///\param[in] NoRuntime - Don't include the runtime headers / gCling
///\param[in] SyntaxOnly - In SyntaxOnly mode
///\brief Include C runtime headers and definitions. ///\param[out] Globals - Global symbols that need to be emitted
/// ///
void IncludeCRuntime(); ///\returns The resulting Transation of initialization.
///\brief Init atexit runtime delegation.
/// ///
void InitAExit(); Transaction* Initialize(bool NoRuntime, bool SyntaxOnly,
llvm::SmallVectorImpl<llvm::StringRef>& Globals);
///\brief The target constructor to be called from both the delegating ///\brief The target constructor to be called from both the delegating
/// constructors. parentInterp might be nullptr. /// constructors. parentInterp might be nullptr.

View File

@ -38,8 +38,7 @@ namespace cling {
IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& diags, IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& diags,
const clang::CompilerInstance& CI): const clang::CompilerInstance& CI):
m_externalIncrementalExecutor(nullptr), m_externalIncrementalExecutor(nullptr)
m_CurrentAtExitModule(0)
#if 0 #if 0
: m_Diags(diags) : m_Diags(diags)
#endif #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 // Register a CXAAtExit function
cling::internal::SpinLockGuard slg(m_AtExitFuncsSpinLock); 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() void unresolvedSymbol()
@ -206,14 +206,6 @@ IncrementalExecutor::runStaticInitializersOnce(const Transaction& T) {
llvm::Module* m = T.getModule(); llvm::Module* m = T.getModule();
assert(m && "Module must not be null"); 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. // We don't care whether something was unresolved before.
m_unresolvedSymbols.clear(); m_unresolvedSymbols.clear();
@ -333,14 +325,15 @@ IncrementalExecutor::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
} }
bool bool
IncrementalExecutor::addSymbol(const char* symbolName, void* symbolAddress) { IncrementalExecutor::addSymbol(const char* Name, void* Addr,
return IncrementalJIT::searchLibraries(symbolName, symbolAddress).second; bool Jit) {
return m_JIT->lookupSymbol(Name, Addr, Jit).second;
} }
void* IncrementalExecutor::getAddressOfGlobal(llvm::StringRef symbolName, void* IncrementalExecutor::getAddressOfGlobal(llvm::StringRef symbolName,
bool* fromJIT /*=0*/) { bool* fromJIT /*=0*/) {
// Return a symbol's address, and whether it was jitted. // 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. // It's not from the JIT if it's in a dylib.
if (fromJIT) if (fromJIT)

View File

@ -114,10 +114,6 @@ namespace cling {
/// ///
AtExitFunctions m_AtExitFuncs; 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. ///\brief Modules to emit upon the next call to the JIT.
/// ///
std::vector<llvm::Module*> m_ModulesToJIT; std::vector<llvm::Module*> m_ModulesToJIT;
@ -208,12 +204,13 @@ namespace cling {
/// Allows runtime declaration of a function passing its pointer for being /// Allows runtime declaration of a function passing its pointer for being
/// used by JIT generated code. /// 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) /// 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. /// @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. ///\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. ///\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; ///\brief Try to resolve a symbol through our LazyFunctionCreators;
/// print an error message if that fails. /// print an error message if that fails.

View File

@ -25,12 +25,6 @@
using namespace llvm; using namespace llvm;
namespace { 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 ///\brief Memory manager providing the lop-level link to the
/// IncrementalExecutor, handles missing or special / replaced symbols. /// IncrementalExecutor, handles missing or special / replaced symbols.
@ -195,17 +189,6 @@ IncrementalJIT::IncrementalJIT(IncrementalExecutor& exe,
llvm::orc::JITSymbol llvm::orc::JITSymbol
IncrementalJIT::getInjectedSymbols(const std::string& Name) const { IncrementalJIT::getInjectedSymbols(const std::string& Name) const {
using JITSymbol = llvm::orc::JITSymbol; 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); auto SymMapI = m_SymbolMap.find(Name);
if (SymMapI != m_SymbolMap.end()) if (SymMapI != m_SymbolMap.end())
return JITSymbol(SymMapI->second, llvm::JITSymbolFlags::Exported); return JITSymbol(SymMapI->second, llvm::JITSymbolFlags::Exported);
@ -214,7 +197,7 @@ IncrementalJIT::getInjectedSymbols(const std::string& Name) const {
} }
std::pair<void*, bool> 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. // FIXME: See comments on DLSym below.
#if !defined(LLVM_ON_WIN32) #if !defined(LLVM_ON_WIN32)
void* Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(Name); 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)); void* Addr = const_cast<void*>(platform::DLSym(Name));
#endif #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); llvm::sys::DynamicLibrary::AddSymbol(Name, InAddr);
return std::make_pair(InAddr, true); return std::make_pair(InAddr, true);
} }

View File

@ -215,9 +215,10 @@ public:
///\brief Get the address of a symbol from the process' loaded libraries. ///\brief Get the address of a symbol from the process' loaded libraries.
/// \param Name - symbol to look for /// \param Name - symbol to look for
/// \param Addr - known address of the symbol that can be cached later use /// \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 /// \returns The address of the symbol and whether it was cached
static std::pair<void*, bool> std::pair<void*, bool>
searchLibraries(llvm::StringRef Name, void* Addr = nullptr); lookupSymbol(llvm::StringRef Name, void* Addr = nullptr, bool Jit = false);
}; };
} // end cling } // end cling
#endif // CLING_INCREMENTAL_EXECUTOR_H #endif // CLING_INCREMENTAL_EXECUTOR_H

View File

@ -57,6 +57,13 @@ using namespace clang;
namespace { 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 static cling::Interpreter::ExecutionResult
ConvertExecutionResult(cling::IncrementalExecutor::ExecutionResult ExeRes) { ConvertExecutionResult(cling::IncrementalExecutor::ExecutionResult ExeRes) {
switch (ExeRes) { switch (ExeRes) {
@ -188,16 +195,30 @@ namespace cling {
handleFrontendOptions(); handleFrontendOptions();
if (!noRuntime) { llvm::SmallVector<llvm::StringRef, 6> Syms;
if (getCI()->getLangOpts().CPlusPlus) Initialize(noRuntime, isInSyntaxOnlyMode(), Syms);
IncludeCXXRuntime();
else
IncludeCRuntime();
}
// Commit the transactions, now that gCling is set up. It is needed for // Commit the transactions, now that gCling is set up. It is needed for
// static initialization in these transactions through local_cxa_atexit(). // static initialization in these transactions through local_cxa_atexit().
for (auto&& I: IncrParserTransactions) for (auto&& I: IncrParserTransactions)
m_IncrParser->commitTransaction(I); 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 // Disable suggestions for ROOT
bool showSuggestions = !llvm::StringRef(ClingStringify(CLING_VERSION)).startswith("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. // Prevents stripping the symbol due to dead-code optimization.
internal::symbol_requester(); internal::symbol_requester();
} }
InitAExit();
} }
///\brief Constructor for the child Interpreter. ///\brief Constructor for the child Interpreter.
@ -278,63 +297,93 @@ namespace cling {
} }
} }
void Interpreter::InitAExit() { Transaction* Interpreter::Initialize(bool NoRuntime, bool SyntaxOnly,
if (isInSyntaxOnlyMode()) llvm::SmallVectorImpl<llvm::StringRef>& Globals) {
return; llvm::SmallString<1024> Buf;
const char* Linkage = getCI()->getLangOpts().CPlusPlus ? "extern \"C\"" : "";
llvm::SmallString<512> Buf;
llvm::raw_svector_ostream Strm(Buf); llvm::raw_svector_ostream Strm(Buf);
Strm << Linkage << " int __cxa_atexit(void (*f) (void*), void*, void*);\n" const clang::LangOptions& LangOpts = getCI()->getLangOpts();
<< Linkage << " int atexit(void(*f)()) {" const void* thisP = static_cast<void*>(this);
"return __cxa_atexit((void(*)(void*))f, nullptr, (void*)"
<< m_Executor.get() << "); }\n"; // FIXME: gCling should be const so assignemnt is a compile time error.
Transaction *T = nullptr; // Currently the name mangling is coming up wrong for the const version
declare(Strm.str(), &T); // (on OS X at least, so probably Linux too) and the JIT thinks the symbol
if (llvm::Module* M = T ? T->getModule() : nullptr) { // is undefined in a child Interpreter. And speaking of children, should
if (const llvm::GlobalValue* GV = M->getNamedValue("atexit")) // gCling actually be thisCling, so a child Interpreter can only access
m_Executor->getPointerToGlobalFromJIT(*GV); // 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() { // Intercept all atexit calls, as the Interpreter and functions will be long
// Set up common declarations which are going to be available // gone when the -native- versions invoke them.
// only at runtime if (!SyntaxOnly) {
// Make sure that the universe won't be included to compile time by using // While __dso_handle is still overriden in the JIT below,
// -D __CLING__ as CompilerInstance's arguments // #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; Strm << "#define __dso_handle ((void*)" << thisP << ")\n";
#ifdef _WIN32
// We have to use the #defined __CLING__ on windows first. // Use __cxa_atexit to intercept all of the following routines
//FIXME: Find proper fix. Strm << Linkage << " int __cxa_atexit(void (*f)(void*), void*, void*);\n";
initializer << "#ifdef __CLING__ \n#endif\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 #endif
initializer << "#include \"cling/Interpreter/RuntimeUniverse.h\"\n"; // Override the native symbols now, before anything can be emitted.
m_Executor->addSymbol("__cxa_atexit", (void*)&local_cxa_atexit, true);
if (!isInSyntaxOnlyMode()) { // __dso_handle is inserted for the link phase, as macro is useless then
// Set up the gCling variable if it can be used m_Executor->addSymbol("__dso_handle", this, true);
initializer << "namespace cling {namespace runtime { "
"cling::Interpreter *gCling=(cling::Interpreter*)"
<< "0x" << std::hex << (uintptr_t)this << " ;} }";
} }
declare(initializer.str());
}
void Interpreter::IncludeCRuntime() { if (m_Opts.Verbose())
// Set up the gCling variable if it can be used llvm::errs() << Strm.str();
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);");
declare("#include \"cling/Interpreter/CValuePrinter.h\""); Transaction *T;
declare(Strm.str(), &T);
return T;
} }
void Interpreter::AddIncludePaths(llvm::StringRef PathStr, const char* Delm) { void Interpreter::AddIncludePaths(llvm::StringRef PathStr, const char* Delm) {
@ -1248,7 +1297,15 @@ namespace cling {
} }
void Interpreter::AddAtExitFunc(void (*Func) (void*), void* Arg) { 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, void Interpreter::GenerateAutoloadingMap(llvm::StringRef inFile,