emscripten-core / emscripten-core/emscripten

Invalid Asyncify stack when using dlopen() with SIDE_MODULE and asyncify imports

Open
#13,049 4 comments 0 reactions 0 assignees View on GitHub
asyncify
Dominant language
C++
Stars
27.6k
Forks
3.6k
Avg merge
1d 1h
Merged PRs (30d)
105

Description

I'm seeing what appears to be an invalid Asyncify call stack when using dlopen to dynamically load a SIDE_MODULE which uses a JavaScript library with asyncify imports. The runtime errors seen are similar to those I see when the Asyncify stack is not large enough (see JS console output below) but I have tried a variety of large stack sizes.

This issue is only present when building as a SIDE_MODULE/MAIN_MODULE and using dlopen. Building the library as a regular callable module and invoking using ccall works as expected. See working example below.

### Minimal reproducer
**dummy_if.cpp (SIDE_MODULE)**
```
#include
#include

extern "C"
{
extern void dummyAsyncImport();
}

extern "C" EMSCRIPTEN_KEEPALIVE
int Dummy_Open(int *handle, void* extra)
{
// Causes RuntimeError: unreachable, on Asyncify.handleSleep wakeUp()
// Removing this, dummyAsyncImport() works asynchronously as expected
// See JS console below
printf("pre dummyAsyncImport\n");

dummyAsyncImport();

// Causes RuntimeError: function signature mismatch, *before* Asyncify.handleSleep wakeUp()
// See JS console below
printf("post dummyAsyncImport\n");

return 0;
}
```

`$ em++ -o dummy_if.wasm -s SIDE_MODULE=1 -s EXPORT_ALL=1 dummy_if.cpp`
`$ mv dummy_if.wasm dummy_if.so`

**library-dummy.js (JS library required by SIDE_MODULE)**
```
mergeInto(LibraryManager.library, {
dummyAsyncImport: function() {
return Asyncify.handleSleep(wakeUp => {
setTimeout(wakeUp, 3000);
});
}
});
```

**dlopen_test.cpp (MAIN_MODULE)**
```
#include
#include

int main()
{
printf("Hello dlopen test\n");

// dlopen
void* handle = dlopen("/dummy_if.so", RTLD_NOW | RTLD_LOCAL);
if (!handle) {
printf("Cannot open library: %s\n", dlerror());
return 1;
}

printf("dlopen'd\n");

// Load symbols
typedef int (*Dummy_Open_t)(int *pHandle, const void *pDetails);
Dummy_Open_t Dummy_Open = (Dummy_Open_t) dlsym(handle, "Dummy_Open");
char *dlsym_error = dlerror();
if (dlsym_error) {
printf("Cannot load symbol Dummy_Open: %s\n", dlsym_error);
dlclose(handle);
return 1;
}

int ifHandle = 0;
int err = Dummy_Open(&ifHandle, NULL);
printf("Dummy_Open err: 0x%08X\n", err);

dlclose(handle);

return 0;
}
```
`$ em++ -o dlopen_test.html -s MAIN_MODULE=1 --preload-file dummy_if.so --use-preload-plugins -s EXPORT_ALL=1 --js-library ./library-dummy.js -s ASYNCIFY=1 -s 'ASYNCIFY_IMPORTS=["dummyAsyncImport"]' -s ASSERTIONS=1 -s ASYNCIFY_STACK_SIZE=40960 dlopen_test.cpp`

### RuntimeErros
![image](https://user-images.githubusercontent.com/45945226/102260127-e8eb7c80-3f07-11eb-866a-0a20bbce73db.png)

### Working example without SIDE_MODULE/dlopen
Build as a entry point library:
`$ em++ -o dummy_if.html -s EXPORT_ALL=1 --js-library ./library-dummy.js -s ASYNCIFY=1 -s 'ASYNCIFY_IMPORTS=["dummyAsyncImport"]' -s ASSERTIONS=1 -s ASYNCIFY_STACK_SIZE=40960 dummy_if.cpp`

Use ccall to invoke from JS console:
`> await ccall("Dummy_Open", 'number', ['number', 'number'], { async : true });`

### Enviroment
```
$ em++ -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 2.0.10
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 445289aa63e1b82b9eea6497fb2d0443813a9d4e)
Target: x86_64-unknown-linux-gnu
Thread model: posix
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.