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,
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.

View File

@ -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)

View File

@ -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.

View File

@ -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);
}

View File

@ -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

View File

@ -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,