diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 4603c0bf6979e..40b5d0f036a2c 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -2320,6 +2320,25 @@ void TROOT::InitInterpreter() fInterpreter->Initialize(); } +//////////////////////////////////////////////////////////////////////////////// +/// Return the CppInterOp API function `funcName` of ROOT's interpreter, starting +/// the interpreter if needed. libCling implements this lookup but is loaded +/// RTLD_LOCAL; exporting it from libCore lets a CppInterOp client such as cppjit +/// pass libCore to Cpp::LoadDispatchAPI() and share ROOT's interpreter instead of +/// loading its own LLVM. + +extern "C" void (*CppGetProcAddress(const char *funcName))() +{ + using GetProcAddress_t = void (*(const char *))(); + static GetProcAddress_t *getProcAddress = [] { + TInterpreter::Instance(); // Sets gInterpreterLib. + auto f = reinterpret_cast(dlsym(gInterpreterLib, "CppGetProcAddress")); + // In rootcling gInterpreterLib is RTLD_DEFAULT, which may find this function. + return f == &CppGetProcAddress ? nullptr : f; + }(); + return getProcAddress ? getProcAddress(funcName) : nullptr; +} + //////////////////////////////////////////////////////////////////////////////// /// Helper function used by TClass::GetClass(). /// This function attempts to load the dictionary for 'classname' diff --git a/core/metacling/test/CMakeLists.txt b/core/metacling/test/CMakeLists.txt index eaec1bfd31863..092d9a9638fbc 100644 --- a/core/metacling/test/CMakeLists.txt +++ b/core/metacling/test/CMakeLists.txt @@ -32,6 +32,12 @@ ROOT_ADD_GTEST( ROOT_ADD_GTEST(TClingLoadUnloadFile TClingLoadUnloadFileTests.cxx LIBRARIES Core RIO) +if(NOT MSVC) + ROOT_ADD_GTEST(TClingDispatch TClingDispatchTests.cxx LIBRARIES Core ${CMAKE_DL_LIBS}) + target_compile_definitions(TClingDispatch PRIVATE ROOT_LIBCORE="$" + ROOT_LIBCLING="$") +endif() + if(pyroot) ROOT_ADD_PYUNITTEST(MetaClingTests MetaClingTests.py) endif() diff --git a/core/metacling/test/TClingDispatchTests.cxx b/core/metacling/test/TClingDispatchTests.cxx new file mode 100644 index 0000000000000..87747191ed2f5 --- /dev/null +++ b/core/metacling/test/TClingDispatchTests.cxx @@ -0,0 +1,30 @@ +#include "TInterpreter.h" + +#include "gtest/gtest.h" + +#include + +using GetProcAddress_t = void (*(const char *))(); + +// A CppInterOp client such as cppjit loads the library that exports +// CppGetProcAddress and looks the API up by name. Through libCore it must get +// the functions of ROOT's interpreter. +TEST(TClingDispatch, CppGetProcAddressFromLibCore) +{ + void *libCore = dlopen(ROOT_LIBCORE, RTLD_LAZY | RTLD_LOCAL); + ASSERT_NE(libCore, nullptr) << dlerror(); + auto getProcAddress = reinterpret_cast(dlsym(libCore, "CppGetProcAddress")); + ASSERT_NE(getProcAddress, nullptr) << dlerror(); + + auto process = reinterpret_cast(getProcAddress("Process")); + ASSERT_NE(process, nullptr); + EXPECT_EQ(process("int TClingDispatchValue = 42;"), 0); + EXPECT_EQ(gInterpreter->Calc("TClingDispatchValue"), 42); + EXPECT_EQ(getProcAddress("NoSuchFunction"), nullptr); + + void *libCling = dlopen(ROOT_LIBCLING, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + ASSERT_NE(libCling, nullptr) << "libCling should be loaded once, by ROOT"; + auto fromLibCling = reinterpret_cast(dlsym(libCling, "CppGetProcAddress")); + ASSERT_NE(fromLibCling, nullptr) << dlerror(); + EXPECT_EQ(getProcAddress("Process"), fromLibCling("Process")); +}