ffi: load libraries from a mounted VFS - #65909
Conversation
The dlopen hook installed while a VFS is mounted always forwarded its flags parameter, so a two-argument process.dlopen() call for a real file system path reached the original implementation with `undefined` as the flags. That coerces to 0, which is not a valid dlopen(2) mode, instead of applying the default flags, and loading any addon from the real file system failed with EINVAL while a VFS was mounted. Signed-off-by: Matteo Collina <hello@matteocollina.com>
|
LETM (Looks Excellent To Me 😃 ) |
|
The only question I have is whether we want to hide the detail that the path may need materializing inside And since |
The operating system's dynamic loader cannot open a library that lives in a mounted virtual file system: the reserved mount path has no real inode. Native addons already handle this in require(): the loader hands their bytes to process.dlopen(), which loads them from a private, self-cleaning image - an anonymous in-memory memfd on Linux. Make ffi.dlopen() and new DynamicLibrary() do the same transparently: a path under the reserved VFS root is detected with a prefix check, its bytes are read from the VFS, and the native constructor loads them from the same kind of image, released right after the load while library.path keeps reporting the virtual path. Libraries on the real file system are unaffected and load directly, and the VFS machinery is only loaded for paths under the reserved root. Since the load happens inside the constructor, the image never outlives the call: nothing is left for dlclose() to clean up and no temporary file lingers on POSIX. The AddonImage materializer moves from an anonymous namespace in node_binding.cc to node_binding.h so that node_ffi.cc can reuse it. Signed-off-by: Matteo Collina <hello@matteocollina.com>
fa3e51d to
dd7d5f0
Compare
The operating system's dynamic loader cannot open a library that lives in a mounted virtual file system: the reserved mount path has no real inode. Native addons already handle this in
require(): the loader hands their bytes toprocess.dlopen(), which loads them from a private, self-cleaning image — an anonymous in-memory memfd on Linux.This makes
ffi.dlopen()andnew ffi.DynamicLibrary()do the same, transparently:AddonImage, moved from an anonymous namespace innode_binding.cctonode_binding.hsonode_ffi.cccan reuse it).library.pathkeeps reporting the virtual path.uv_dlopen()on POSIX (in-memory memfd on Linux, so nothing touches the file system at all), and there is nothing left fordlclose()to clean up or reference-count. Windows retains the delete-on-close handle for the process lifetime, exactly as for addons.dlopenBinary().Bug fix included
Writing the test exposed a pre-existing bug, fixed in the first commit: the dlopen hook installed while a VFS is mounted always forwarded its
flagsparameter, so a two-argumentprocess.dlopen()call for a real file-system path reached the original implementation withundefinedas the flags. That coerces to0, which is not a validdlopen(2)mode, and loading any addon from the real file system failed with EINVAL while a VFS was mounted.