erts: Allow erlang:load_nif/2 to load a NIF library from an in-memory binary#10996
Open
saleyn wants to merge 1 commit into
Open
erts: Allow erlang:load_nif/2 to load a NIF library from an in-memory binary#10996saleyn wants to merge 1 commit into
saleyn wants to merge 1 commit into
Conversation
Contributor
CT Test Results 3 files 136 suites 52m 23s ⏱️ Results for commit 67cf2fa. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts// Erlang/OTP Github Action Bot |
2ba4729 to
059819e
Compare
Contributor
|
This could be used for Windows: https://github.com/fancycode/MemoryModule |
23993c1 to
1756f39
Compare
Contributor
Author
After I get the tests pass on all Unix/Mac flavors, I welcome a PR on top of mine to add the Windows support. |
37183a1 to
8d459f0
Compare
6832198 to
a0d8c34
Compare
a63e452 to
811316e
Compare
Add support for passing `#{memory => NifSO::binary(),
filename => string():binary()}` as the first argument to erlang:load_nif/2.
When a map is given with the 'memory' key/value present, the NIF shared
object is loaded directly from the binary image in memory instead of
from the file system. If the map only contains the 'filename' key/value
it'll revert to the default behavior of loading the SO from file.
This addition is needed so that escripts can package SO dependencies
inside and be distributed as a single executable.
Implementation
--------------
* erts_dlopen_mem() — new static helper in erl_nif.c (Linux/POSIX only).
On Linux >= 3.17 it uses memfd_create(2) to create an anonymous
in-memory file, writes the SO image into it, and calls dlopen(3) via
/proc/self/fd/<fd>. On older Linux kernels and other POSIX systems it
falls back to shm_open(3) + /dev/shm/<name>. The implementation is
derived from the https://github.com/saleyn/memfd_create proof of
concept.
* erts_load_nif() — extended argument parsing:
- Plain filename() continues to work unchanged.
- #{memory => Binary, filename => Filename} map: Binary is extracted via
erts_get_aligned_binary_bytes(), passed to erts_dlopen_mem(), and
the resulting dlopen handle is used directly. The Filename string
serves only as a label inside the memory-backed file descriptor.
- The open-from-memory step is performed before the existing else-if
validation chain (version checks, module-name check, create_lib)
so both code paths share the same validation and bookkeeping.
- erts_sys_ddll_open() is skipped (via !load_from_mem guard) when a
handle was already obtained from memory.
* The feature is guarded by #if defined(HAVE_DLOPEN) && defined(__unix__).
On unsupported platforms load_nif/2 returns {error,{load_failed,…}}.
Testing
-------
* nif_SUITE: new test case load_nif_from_mem
- Happy path: reads nif_mod.1.so into a binary, calls
erlang:load_nif(#{filename => Path, memory => Binary}, []) via
nif_mod:load_nif_lib_from_mem/3, and asserts lib_version() == 1.
- Error path: non-binary second element returns {error,{bad_lib,_}}.
* nif_mod.erl: new exported helper load_nif_lib_from_mem/3 so that the
load_nif call site lives inside the NIF module (required by the BIF).
811316e to
67cf2fa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add support for passing
#{memory => NifSO::binary(), filename => Filename::string()}as the first argument to erlang:load_nif/2. When a map is given, the NIF shared object is loaded directly from the binary image in memory instead of from the file system.Why is this needed?
Presently build tools (i.e.
rebar3,mix) cannot bundle all dependencies into a single executable archive escript (a.k.a. escriptizing) that have dependencies with NIF shared object libraries. This PR addresses that gap, and extends theerlang:load_nif/2call with the missing functionality. After it's merged, the build tools will be able to be modified to take advantage of it. See #4476 issue.Implementation
erts_dlopen_mem() — new static helper in erl_nif.c (Linux/POSIX only). On Linux >= 3.17 it uses memfd_create(2) to create an anonymous in-memory file, writes the SO image into it, and calls dlopen(3) via /proc/self/fd/. On older Linux kernels and other POSIX systems it falls back to shm_open(3) + /dev/shm/. The implementation is derived from the https://github.com/saleyn/memfd_create proof of concept.
erts_load_nif() — extended argument parsing:
The feature is guarded by #if defined(HAVE_DLOPEN) && defined(unix). On unsupported platforms load_nif/2 returns {error,{load_failed,…}}.
Testing
nif_SUITE: new test case load_nif_from_mem
nif_mod.erl: new exported helper load_nif_lib_from_mem/3 so that the load_nif call site lives inside the NIF module (required by the BIF).