emscripten-core / emscripten-core/emscripten
[Dynamic link] Error loading main module if using C++ class from side module
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
### The problem
I have a main module and a side module written in C++. The main module uses a C++ class with virtual function which is defined in the side module then the main module won't load successfully
The error is like
```
Uncaught (in promise) RuntimeError: Aborted(Assertion failed: undefined symbol '_ZTV5Child'. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment)
at abort (main.js:723:11)
at assert (main.js:388:5)
at reportUndefinedSymbols (main.js:2069:11)
at loadDylibs (main.js:2083:9)
at receiveInstance (main.js:893:5)
at receiveInstantiationResult (main.js:917:5)
```
And this is a runnable code [test_dylink_no_autoload.zip](https://github.com/emscripten-core/emscripten/files/14331411/test_dylink_no_autoload.zip)
I also paste demo code below for convinience:
### Demo code
I write a demo with these files:
1. main.cpp
```C++
// main.cpp
#include
#include
#include "side.h"
#include
int sidey();
int main() {
return 0;
}
EMSCRIPTEN_KEEPALIVE extern "C" void test_sidey() {
dlopen("/libside.wasm", RTLD_NOW|RTLD_GLOBAL);
printf("sidey: %d\n", sidey());
}
EMSCRIPTEN_KEEPALIVE extern "C" void test_child() {
dlopen("/libside.wasm", RTLD_NOW|RTLD_GLOBAL);
// 1. If comment out the following lines this demo should work
// 2. And if Base::side is not virtual the following code also works
Child child;
child.side();
}
```
3. side.cpp & side.h
```C++
// side.h
#pragma once
class Base {
public:
virtual void side();
};
class Child :public Base{
public:
void side();
};
```
```C++
// side.cpp
#include
#include "side.h"
int sidey() { return 42; }
void Base::side() {
std::cout << 55 << std::endl;
}
void Child::side() {
std::cout << 66 << std::endl;
}
```
5. pre.js
```JavaScript
Module.postRun = function() {
let readAsync = (url, onload, onerror) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = () => {
if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
onload(xhr.response);
return;
}
onerror();
};
xhr.onerror = onerror;
xhr.send(null);
}
readAsync("/libside.wasm", function(arrayBuffer) {
Module.FS.writeFile("/libside.wasm", new Uint8Array(arrayBuffer));
console.log('Write side module finish')
});
}
```
Build:
```Bash
emcc -sSIDE_MODULE side.cpp -o libside.wasm
emcc -sMAIN_MODULE=2 main.cpp libside.wasm -sNO_AUTOLOAD_DYLIBS -sEXPORTED_RUNTIME_METHODS="FS" -sASSERTIONS=1 --pre-js=pre.js -o main.html
```
Contributor guide
Assessment
This issue has not been assessed yet.