From 6ec4386776bf23b15043f9ab944cf0cc66e28e8f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 29 Apr 2017 17:06:22 -0300 Subject: [PATCH 1/2] constants: add dlopen flags Let's add constants for dlopen flags, which are needed for dlopen's flag passing, implemented in next commit. Signed-off-by: Ezequiel Garcia --- doc/api/os.md | 37 +++++++++++++++++++++++++ lib/constants.js | 1 + src/node_constants.cc | 32 +++++++++++++++++++++ test/parallel/test-binding-constants.js | 5 ++-- 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/doc/api/os.md b/doc/api/os.md index 28eff6a13f0b60..1bef5c5501688b 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -1170,6 +1170,43 @@ The following error codes are specific to the Windows operating system: +### dlopen Constants + +If available on the operating system, the following constants +are exported in `os.constants.dlopen`. See dlopen(3) for detailed +information. + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConstantDescription
RTLD_LAZYPerform lazy binding. Node.js sets this flag by default.
RTLD_NOWResolve all undefined symbols in the library before dlopen(3) + returns.
RTLD_GLOBALSymbols defined by the library will be made available for symbol + resolution of subsequently loaded libraries.
RTLD_LOCALThe converse of RTLD_GLOBAL. This is the default behavior if neither + flag is specified.
RTLD_DEEPBINDMake a self-contained library use its own symbols in preference to + symbols from previously loaded libraries.
+ ### libuv Constants diff --git a/lib/constants.js b/lib/constants.js index dbd8dbc4d5a05d..3336fd8d7fc210 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -27,6 +27,7 @@ // Deprecation Code: DEP0008 const constants = process.binding('constants'); Object.assign(exports, + constants.os.dlopen, constants.os.errno, constants.os.signals, constants.fs, diff --git a/src/node_constants.cc b/src/node_constants.cc index f8055f01e6d631..b8b97b21f9ea39 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -44,6 +44,10 @@ # endif // !OPENSSL_NO_ENGINE #endif +#if defined(__POSIX__) +#include +#endif + namespace node { using v8::Local; @@ -1238,6 +1242,28 @@ void DefineZlibConstants(Local target) { NODE_DEFINE_CONSTANT(target, Z_DEFAULT_LEVEL); } +void DefineDLOpenConstants(Local target) { +#ifdef RTLD_LAZY + NODE_DEFINE_CONSTANT(target, RTLD_LAZY); +#endif + +#ifdef RTLD_NOW + NODE_DEFINE_CONSTANT(target, RTLD_NOW); +#endif + +#ifdef RTLD_GLOBAL + NODE_DEFINE_CONSTANT(target, RTLD_GLOBAL); +#endif + +#ifdef RTLD_LOCAL + NODE_DEFINE_CONSTANT(target, RTLD_LOCAL); +#endif + +#ifdef RTLD_DEEPBIND + NODE_DEFINE_CONSTANT(target, RTLD_DEEPBIND); +#endif +} + } // anonymous namespace void DefineConstants(v8::Isolate* isolate, Local target) { @@ -1267,6 +1293,10 @@ void DefineConstants(v8::Isolate* isolate, Local target) { CHECK(zlib_constants->SetPrototype(env->context(), Null(env->isolate())).FromJust()); + Local dlopen_constants = Object::New(isolate); + CHECK(dlopen_constants->SetPrototype(env->context(), + Null(env->isolate())).FromJust()); + DefineErrnoConstants(err_constants); DefineWindowsErrorConstants(err_constants); DefineSignalConstants(sig_constants); @@ -1274,11 +1304,13 @@ void DefineConstants(v8::Isolate* isolate, Local target) { DefineOpenSSLConstants(crypto_constants); DefineCryptoConstants(crypto_constants); DefineZlibConstants(zlib_constants); + DefineDLOpenConstants(dlopen_constants); // Define libuv constants. NODE_DEFINE_CONSTANT(os_constants, UV_UDP_REUSEADDR); NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_EXCL); + os_constants->Set(OneByteString(isolate, "dlopen"), dlopen_constants); os_constants->Set(OneByteString(isolate, "errno"), err_constants); os_constants->Set(OneByteString(isolate, "signals"), sig_constants); target->Set(OneByteString(isolate, "os"), os_constants); diff --git a/test/parallel/test-binding-constants.js b/test/parallel/test-binding-constants.js index fcc95c21a3f7ea..aaf0ebde5230a8 100644 --- a/test/parallel/test-binding-constants.js +++ b/test/parallel/test-binding-constants.js @@ -9,7 +9,8 @@ assert.deepStrictEqual( ); assert.deepStrictEqual( - Object.keys(constants.os).sort(), ['UV_UDP_REUSEADDR', 'errno', 'signals'] + Object.keys(constants.os).sort(), ['UV_UDP_REUSEADDR', 'dlopen', 'errno', + 'signals'] ); // Make sure all the constants objects don't inherit from Object.prototype @@ -26,5 +27,5 @@ function test(obj) { [ constants, constants.crypto, constants.fs, constants.os, constants.zlib, - constants.os.errno, constants.os.signals + constants.os.dlopen, constants.os.errno, constants.os.signals ].forEach(test); From 3ca00e7dc2fe2ed4fa270d51577a0285a48f438c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 14 Jun 2017 16:05:01 -0300 Subject: [PATCH 2/2] src: add support to pass flags to dlopen This commit introduces an optional parameter for process.dlopen(), allowing to pass dlopen flags (using values from os.constants.dlopen). If no flags are passed, the default behavior is to load the library with RTLD_LAZY (perform lazy binding) and RTLD_LOCAL (symbols are available only locally). Signed-off-by: Ezequiel Garcia --- doc/api/process.md | 45 +++++++++++++ src/node.cc | 81 ++++++++++++++++++++---- test/addons/dlopen-ping-pong/binding.cc | 48 ++++++++++++++ test/addons/dlopen-ping-pong/binding.gyp | 25 ++++++++ test/addons/dlopen-ping-pong/ping.c | 9 +++ test/addons/dlopen-ping-pong/test.js | 23 +++++++ 6 files changed, 217 insertions(+), 14 deletions(-) create mode 100644 test/addons/dlopen-ping-pong/binding.cc create mode 100644 test/addons/dlopen-ping-pong/binding.gyp create mode 100644 test/addons/dlopen-ping-pong/ping.c create mode 100644 test/addons/dlopen-ping-pong/test.js diff --git a/doc/api/process.md b/doc/api/process.md index e24bf19d9d2f44..82edcf9436c8eb 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -638,6 +638,48 @@ process's [`ChildProcess.disconnect()`][]. If the Node.js process was not spawned with an IPC channel, `process.disconnect()` will be `undefined`. +## process.dlopen(module, filename[, flags]) + + +* `module` {Object} +* `filename` {string} +* `flags` {os.constants.dlopen}. Defaults to `os.constants.dlopen.RTLD_LAZY`. + +The `process.dlopen()` method allows to dynamically load shared +objects. It is primarily used by `require()` to load +C++ Addons, and should not be used directly, except in special +cases. In other words, [`require()`][] should be preferred over +`process.dlopen()`, unless there are specific reasons. + +The `flags` argument is an integer that allows to specify dlopen +behavior. See the [`os.constants.dlopen`][] documentation for details. + +If there are specific reasons to use `process.dlopen()` (for instance, +to specify dlopen flags), it's often useful to use [`require.resolve()`][] +to look up the module's path. + +*Note*: An important drawback when calling `process.dlopen()` is that the +`module` instance must be passed. Functions exported by the C++ Addon will +be accessible via `module.exports`. + +The example below shows how to load a C++ Addon, named as `binding`, +that exports a `foo` function. All the symbols will be loaded before +the call returns, by passing the `RTLD_NOW` constant. In this example +the constant is assumed to be available. + +```js +const os = require('os'); +process.dlopen(module, require.resolve('binding'), + os.constants.dlopen.RTLD_NOW); +module.exports.foo(); +``` + ## process.emitWarning(warning[, options])