mirror of
https://github.com/fluencelabs/musl
synced 2025-04-25 07:12: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)
|
||||
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
|
||||
lazy-loading, where the developer can handle load failures. We could also easily
|
||||
implement `dlopen` / `dlsym` / `dlclose`.
|
||||
lazy-loading, where the developer can handle load failures. We can easily
|
||||
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.
|
||||
|
||||
@ -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
|
||||
lazy-load and lazy-compile each function as needed, and even unload them when
|
||||
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_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) {
|
||||
this.value = value;
|
||||
this.message = 'Terminating WebAssembly';
|
||||
@ -319,11 +330,7 @@ var stdio = (function() {
|
||||
stdout_buf += String.fromCharCode(character);
|
||||
return character;
|
||||
},
|
||||
puts: function(str) {
|
||||
for (var i = 0; heap_uint8[str + i] != 0; ++i)
|
||||
stdout_buf += String.fromCharCode(heap_uint8[str + i]);
|
||||
stdout_buf += '\n';
|
||||
},
|
||||
puts: function(str) { stdout_buf += stringFromHeap(str) + '\n'; },
|
||||
ungetc: NYI('ungetc'),
|
||||
|
||||
// Direct input/output.
|
||||
@ -539,6 +546,10 @@ var unix = (function() {
|
||||
var OPEN_MAX = 256;
|
||||
var open_files = new Uint8Array(OPEN_MAX);
|
||||
|
||||
var dlfcn = {};
|
||||
var dlfcn_handle_to_filename = {};
|
||||
var dlfcn_max_handle = 0;
|
||||
|
||||
return {
|
||||
// <dlfcn.h> constants.
|
||||
RTLD_LAZY: 1,
|
||||
@ -552,10 +563,47 @@ var unix = (function() {
|
||||
RTLD_DI_LINKMAP: 2,
|
||||
|
||||
// <dlfcn.h>
|
||||
dlclose: NYI('dlclose'),
|
||||
dlerror: NYI('dlerror'),
|
||||
dlopen: NYI('dlopen'),
|
||||
dlsym: NYI('dlsym'),
|
||||
dlclose: function(handle) {
|
||||
var filename = dlfcn_handle_to_filename[handle];
|
||||
if (!filename) NYI('dlclose of invalid handle')();
|
||||
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'),
|
||||
dlinfo: NYI('dlinfo'),
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user