mirror of
https://github.com/fluencelabs/musl
synced 2025-04-25 15:22:15 +00:00
dlfcn.h implementation and detailed example.
This commit is contained in:
parent
13f9666606
commit
acc1d6453b
81
README.md
81
README.md
@ -99,8 +99,9 @@ A WebAssembly module with un-met imports will throw. This can be handled, add
|
|||||||
the missing function as a stub to FFI, and then load again (loop until success)
|
the missing function as a stub to FFI, and then load again (loop until success)
|
||||||
but it's silly. If WebAssembly modules were loadable, imports inspectable, and
|
but it's silly. If WebAssembly modules were loadable, imports inspectable, and
|
||||||
FFI object provided later then we'd be better off. We could implement very fancy
|
FFI object provided later then we'd be better off. We could implement very fancy
|
||||||
lazy-loading, where the developer can handle load failures. We could also easily
|
lazy-loading, where the developer can handle load failures. We can easily
|
||||||
implement `dlopen` / `dlsym` / `dlclose`.
|
implement `dlopen` / `dlsym` / `dlclose` as demonstrated by the `<dlfcn.h>`
|
||||||
|
example below.
|
||||||
|
|
||||||
It would also be good to be able to specify compilation / execution separately.
|
It would also be good to be able to specify compilation / execution separately.
|
||||||
|
|
||||||
@ -162,3 +163,79 @@ Developers are in control: they can do the equivalent of `-ffunction-sections`
|
|||||||
and `-fdata-sections` but emit one `.wasm` file per section. This allows them to
|
and `-fdata-sections` but emit one `.wasm` file per section. This allows them to
|
||||||
lazy-load and lazy-compile each function as needed, and even unload them when
|
lazy-load and lazy-compile each function as needed, and even unload them when
|
||||||
the program doesn't need them anymore.
|
the program doesn't need them anymore.
|
||||||
|
|
||||||
|
|
||||||
|
## `<dlfcn.h>` example
|
||||||
|
|
||||||
|
This example doesn't use `musl.wasm`, it currently only uses `wasm.js`. musl
|
||||||
|
could be used for this, it would be much cleaner (e.g. `dlerror` could work and
|
||||||
|
return `const char *` as it should), but it requires hooking up syscalls
|
||||||
|
properly.
|
||||||
|
|
||||||
|
Create `dlhello.c`:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
typedef void (*world_type)();
|
||||||
|
|
||||||
|
void *handle = dlopen("dlworld.wasm", RTLD_NOW);
|
||||||
|
if (!handle) {
|
||||||
|
puts("dlopen failed:");
|
||||||
|
puts(dlerror());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
dlerror();
|
||||||
|
world_type world = (world_type)dlsym(handle, "world");
|
||||||
|
const char *err = dlerror();
|
||||||
|
if (err) {
|
||||||
|
puts("dlsym failed:");
|
||||||
|
puts(err);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
world();
|
||||||
|
|
||||||
|
dlclose(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And `dlworld.c`:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <stdio.h>
|
||||||
|
void world() { puts("World!"); }
|
||||||
|
```
|
||||||
|
|
||||||
|
Compile the programs:
|
||||||
|
|
||||||
|
```
|
||||||
|
clang -S -O2 --target=wasm32-unknown-unknown ./dlhello.c
|
||||||
|
clang -S -O2 --target=wasm32-unknown-unknown ./dlworld.c
|
||||||
|
s2wasm dlhello.s -o dlhello.wast
|
||||||
|
s2wasm dlworld.s -o dlworld.wast
|
||||||
|
sexpr-wasm dlhello.wast -o dlhello.wasm
|
||||||
|
sexpr-wasm dlworld.wast -o dlworld.wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute it:
|
||||||
|
|
||||||
|
```
|
||||||
|
d8 --expose-wasm musl/arch/wasm32/wasm.js -- dlhello.wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this currently doesn't work because the `dlsym` implementation returns
|
||||||
|
the function from another module, and the implementation puts the functions in
|
||||||
|
different tables. We could fix this by:
|
||||||
|
|
||||||
|
* Forcing developers to use a function such as `dlcall` and provide handles for
|
||||||
|
the module and symbol.
|
||||||
|
* Map functions from all module into the same table.
|
||||||
|
* Map functions from other modules into the current one when `dlsym` is invoked,
|
||||||
|
e.g. adding new functions to the `_WASMEXP_` instance. This also requires
|
||||||
|
tracking `dlclose` properly.
|
||||||
|
@ -25,6 +25,17 @@ var HEAP_SIZE_BYTES = 1 << 20;
|
|||||||
var heap = new ArrayBuffer(HEAP_SIZE_BYTES);
|
var heap = new ArrayBuffer(HEAP_SIZE_BYTES);
|
||||||
var heap_uint8 = new Uint8Array(heap);
|
var heap_uint8 = new Uint8Array(heap);
|
||||||
|
|
||||||
|
// Heap access helpers.
|
||||||
|
function charFromHeap(ptr) { return String.fromCharCode(heap_uint8[ptr]); }
|
||||||
|
function stringFromHeap(ptr) {
|
||||||
|
var str = '';
|
||||||
|
for (var i = ptr; heap_uint8[i] != 0; ++i)
|
||||||
|
str += charFromHeap(i);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exceptions.
|
||||||
|
|
||||||
function TerminateWasmException(value) {
|
function TerminateWasmException(value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.message = 'Terminating WebAssembly';
|
this.message = 'Terminating WebAssembly';
|
||||||
@ -319,11 +330,7 @@ var stdio = (function() {
|
|||||||
stdout_buf += String.fromCharCode(character);
|
stdout_buf += String.fromCharCode(character);
|
||||||
return character;
|
return character;
|
||||||
},
|
},
|
||||||
puts: function(str) {
|
puts: function(str) { stdout_buf += stringFromHeap(str) + '\n'; },
|
||||||
for (var i = 0; heap_uint8[str + i] != 0; ++i)
|
|
||||||
stdout_buf += String.fromCharCode(heap_uint8[str + i]);
|
|
||||||
stdout_buf += '\n';
|
|
||||||
},
|
|
||||||
ungetc: NYI('ungetc'),
|
ungetc: NYI('ungetc'),
|
||||||
|
|
||||||
// Direct input/output.
|
// Direct input/output.
|
||||||
@ -539,6 +546,10 @@ var unix = (function() {
|
|||||||
var OPEN_MAX = 256;
|
var OPEN_MAX = 256;
|
||||||
var open_files = new Uint8Array(OPEN_MAX);
|
var open_files = new Uint8Array(OPEN_MAX);
|
||||||
|
|
||||||
|
var dlfcn = {};
|
||||||
|
var dlfcn_handle_to_filename = {};
|
||||||
|
var dlfcn_max_handle = 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// <dlfcn.h> constants.
|
// <dlfcn.h> constants.
|
||||||
RTLD_LAZY: 1,
|
RTLD_LAZY: 1,
|
||||||
@ -552,10 +563,47 @@ var unix = (function() {
|
|||||||
RTLD_DI_LINKMAP: 2,
|
RTLD_DI_LINKMAP: 2,
|
||||||
|
|
||||||
// <dlfcn.h>
|
// <dlfcn.h>
|
||||||
dlclose: NYI('dlclose'),
|
dlclose: function(handle) {
|
||||||
dlerror: NYI('dlerror'),
|
var filename = dlfcn_handle_to_filename[handle];
|
||||||
dlopen: NYI('dlopen'),
|
if (!filename) NYI('dlclose of invalid handle')();
|
||||||
dlsym: NYI('dlsym'),
|
dlfcn[filename].refcount -= 1;
|
||||||
|
if (dlfcn[filename].refcount == 0)
|
||||||
|
dlfcn[filename] = undefined;
|
||||||
|
return 0; },
|
||||||
|
dlerror: function() {
|
||||||
|
// TODO: implement error handling.
|
||||||
|
return 0; },
|
||||||
|
dlopen: function(filename, flags) {
|
||||||
|
if (!filename) NYI('dlopen(NULL, ...);')();
|
||||||
|
var fs = stringFromHeap(filename);
|
||||||
|
if (dlfcn[fs]) {
|
||||||
|
dlfcn[fs].refcount += 1;
|
||||||
|
return dlfcn[fs].handle;
|
||||||
|
}
|
||||||
|
if (flags & unix.RTLD_LAZY) NYI('dlopen with flag RTLD_LAZY')();
|
||||||
|
if (~flags & unix.RTLD_NOW) NYI('dlopen without flag RTDL_NOW')();
|
||||||
|
if (flags & unix.RTLD_NOLOAD) NYI('dlopen with flag RTLD_NOLOAD')();
|
||||||
|
if (flags & unix.RTLD_NODELETE) NYI('dlopen with flag RTLD_NODELETE')();
|
||||||
|
if (flags & unix.RTLD_GLOBAL) NYI('dlopen with flag RTLD_GLOBAL')();
|
||||||
|
// TODO: other flags.
|
||||||
|
var handle = ++dlfcn_max_handle;
|
||||||
|
dlfcn[fs] = {
|
||||||
|
refcount: 0,
|
||||||
|
module: load_wasm(fs),
|
||||||
|
handle: handle
|
||||||
|
};
|
||||||
|
dlfcn_handle_to_filename[handle] = fs;
|
||||||
|
return handle; },
|
||||||
|
dlsym: function(handle, symbol) {
|
||||||
|
var filename = dlfcn_handle_to_filename[handle];
|
||||||
|
if (!filename) NYI('dlsym of invalid handle')();
|
||||||
|
if (!symbol) NYI('dlsym of NULL symbol')();
|
||||||
|
var ss = stringFromHeap(symbol);
|
||||||
|
// TODO: error handling when module doesn't contain symbol.
|
||||||
|
for (var m in dlfcn[filename].module)
|
||||||
|
if (m == ss)
|
||||||
|
return m;
|
||||||
|
NYI('dlsym with symbol not found in handle')(); },
|
||||||
dladdr: NYI('dladdr'),
|
dladdr: NYI('dladdr'),
|
||||||
dlinfo: NYI('dlinfo'),
|
dlinfo: NYI('dlinfo'),
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user