From 4fae6e3904f74e1ce1816abcae1aee6799f72e11 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 22 Feb 2018 13:35:26 +0100 Subject: [PATCH] src: make process.dlopen() load well-known symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Look for symbol `node_register_module_v${NODE_MODULE_VERSION}` if the add-on didn't self-register. This can be used to create add-ons that support multiple Node.js versions from a single shared object. PR-URL: https://github.com/nodejs/node/pull/18934 Reviewed-By: Colin Ihrig Reviewed-By: Daniel Bevenius Reviewed-By: Franziska Hinkelmann Reviewed-By: James M Snell Reviewed-By: Joyee Cheung Reviewed-By: Matheus Marchini Reviewed-By: Tobias Nießen --- src/node.cc | 29 +++++++++++++++++++++++++++-- src/node.h | 3 +++ test/addons/hello-world/binding.cc | 10 +++++++--- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/node.cc b/src/node.cc index 322a84df5ca34e..35a1d5cb598d5b 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2483,6 +2483,7 @@ struct DLib { inline bool Open(); inline void Close(); + inline void* GetSymbolAddress(const char* name); const std::string filename_; const int flags_; @@ -2510,6 +2511,10 @@ void DLib::Close() { dlclose(handle_); handle_ = nullptr; } + +void* DLib::GetSymbolAddress(const char* name) { + return dlsym(handle_, name); +} #else // !__POSIX__ bool DLib::Open() { int ret = uv_dlopen(filename_.c_str(), &lib_); @@ -2527,8 +2532,23 @@ void DLib::Close() { uv_dlclose(&lib_); handle_ = nullptr; } + +void* DLib::GetSymbolAddress(const char* name) { + void* address; + if (0 == uv_dlsym(&lib_, name, &address)) return address; + return nullptr; +} #endif // !__POSIX__ +using InitializerCallback = void (*)(Local exports, + Local module, + Local context); + +inline InitializerCallback GetInitializerCallback(DLib* dlib) { + const char* name = "node_register_module_v" STRINGIFY(NODE_MODULE_VERSION); + return reinterpret_cast(dlib->GetSymbolAddress(name)); +} + // DLOpen is process.dlopen(module, filename, flags). // Used to load 'module.node' dynamically shared objects. // @@ -2583,10 +2603,15 @@ static void DLOpen(const FunctionCallbackInfo& args) { } if (mp == nullptr) { - dlib.Close(); - env->ThrowError("Module did not self-register."); + if (auto callback = GetInitializerCallback(&dlib)) { + callback(exports, module, context); + } else { + dlib.Close(); + env->ThrowError("Module did not self-register."); + } return; } + if (mp->nm_version == -1) { if (env->EmitNapiWarning()) { if (ProcessEmitWarning(env, "N-API is an experimental feature and could " diff --git a/src/node.h b/src/node.h index 89dbdfc727b0c5..44d9ca9c77c18b 100644 --- a/src/node.h +++ b/src/node.h @@ -532,6 +532,9 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); } \ } +// Usage: `NODE_MODULE(NODE_GYP_MODULE_NAME, InitializerFunction)` +// If no NODE_MODULE is declared, Node.js looks for the well-known +// symbol `node_register_module_v${NODE_MODULE_VERSION}`. #define NODE_MODULE(modname, regfunc) \ NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) diff --git a/test/addons/hello-world/binding.cc b/test/addons/hello-world/binding.cc index 944f5631956d15..ba6a22d7196d26 100644 --- a/test/addons/hello-world/binding.cc +++ b/test/addons/hello-world/binding.cc @@ -6,8 +6,12 @@ void Method(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world")); } -void init(v8::Local exports) { +#define CONCAT(a, b) CONCAT_HELPER(a, b) +#define CONCAT_HELPER(a, b) a##b +#define INITIALIZER CONCAT(node_register_module_v, NODE_MODULE_VERSION) + +extern "C" NODE_MODULE_EXPORT void INITIALIZER(v8::Local exports, + v8::Local module, + v8::Local context) { NODE_SET_METHOD(exports, "hello", Method); } - -NODE_MODULE(NODE_GYP_MODULE_NAME, init)