Closed orsinium closed 5 months ago
I think the simplest solution here is to use the --no-entry
command line flag.
Alternatively you can pass -sEXPORTED_FUNCTIONS=_boot
(any list doesn't doesn't include main will work here).
I tried to use --no-entry
and it (supposedly) caused some issues with pocketpy: https://github.com/pocketpy/pocketpy/issues/279
@szdytom said:
we cannot set
--no-entry
for a C++ source because the linker needs to call the constructor of globals.https://github.com/pocketpy/pocketpy/issues/279#issuecomment-2179641284
UPD: And I think they are right: compiling with -Wl,--no-entry
seems to produce a binary without the _start
export. Only some custom emscripten_stack_*
exports.
Also, I tried setting EXPORTED_FUNCTIONS
. It does compile without main
but then the binary has env.__main_argc_argv
import. I'm not sure what it is and why it is there now. Searching online gives only one hit:
https://bugs.archlinux.org/task/75269
UPD: Turns out, it compiles not because I set EXPORTED_FUNCTIONS
but because I also set ERROR_ON_UNDEFINED_SYMBOLS=0
.
When you use --no-entry
you also need to call the _initialize
export to run the static constructors. This need to be called before you call any other export. This is part of the "reactor" ABI.
I would strongly suggest --no-extry
as the solution here since that tells emscripten you are building a reactor.
I would stringly recommend against using ERROR_ON_UNDEFINED_SYMBOLS=0
as that can lead to undefined symbols at rutime.
There is no _initialize
export as well.
Code:
#include "../vendor/pocketpy/include/pocketpy.h"
using namespace pkpy;
#define WASM_EXPORT(NAME) __attribute__((export_name(NAME)))
WASM_EXPORT("boot")
void boot()
{
VM *vm = new VM();
vm->exec("a = [1, 2, 3]");
}
Build command:
em++ ./src/main.cpp pocketpy/*.o -Ivendor/pocketpy/include -fignore-exceptions -frtti -Os -s STANDALONE_WASM -Wl,--no-entry -o main.wasm
wasm-objdump -x --section=export ./main.wasm
:
Export[5]:
- memory[0] -> "memory"
- func[8] <boot> -> "boot"
- table[0] -> "__indirect_function_table"
- func[2161] <_emscripten_stack_restore> -> "_emscripten_stack_restore"
- func[2162] <emscripten_stack_get_current> -> "emscripten_stack_get_current"
I'm no expert in WASM, but maybe you should add -mexec-model=reactor
flag? I'm no sure. Here are some reference I found that might be related to this topic:
How can I set this flag through emscripten? I know that it should generally work but AFAIK it's the clang's flag, not emsdk's.
$ em++ ./src/main.cpp pocketpy/*.o -Ivendor/pocketpy/include -fignore-exceptions -frtti -Os -s STANDALONE_WASM -Wl,--no-entry -mexec-model=reactor -o main.wasm
clang++: error: unsupported option '-mexec-model=' for target 'wasm32-unknown-emscripten'
em++: error: '/home/gram/Documents/emsdk/upstream/bin/clang++ -target wasm32-unknown-emscripten -fignore-exceptions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/home/gram/Documents/emsdk/upstream/emscripten/cache/sysroot -DEMSCRIPTEN -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compat -Ivendor/pocketpy/include -fignore-exceptions -frtti -Os -mexec-model=reactor ./src/main.cpp -c -o /tmp/emscripten_temp_dnw4pz3k/main_0.o' failed (returned 1)
I know that the flags that wasm4 uses for compiling wasm binaries work great, and they indeed set -mexec-model=reactor
:
https://github.com/aduros/wasm4/blob/main/cli/assets/templates/c/Makefile#L24
However, they use wasi-sdk instead of emscripten. I tried to use wasi-sdk earlier as well to compile pocketpy, but so far emscripten brought me seemingly closer to success.
Sorry the --no-entry
flag is an emcc flag.. you don't need to use -Wl,
before it.
e.g:
$ ./emcc -Os test/hello_world.c -o out.wasm --no-entry
$ wasm-objdump -x out.wasm | grep _init
- func[0] sig=0 <_initialize>
- func[0] <_initialize> -> "_initialize"
- elem[1] = ref.func:0 <_initialize>
- func[0] size=2 <_initialize>
This tells emscripten (not just the linker) that you are building a reactor.
It works! Thank you! You both were very helpful, and you made Python on Firefly Zero possible. FOSS needs more people like you :)
Summary: I set
IGNORE_MISSING_MAIN
option but compilation still fails withundefined symbol: main
.Code (main.cpp):
Build command:
Output:
The problem I'm trying to solve is that if I define an empty
main
function, emscripten will insertproc_exit
andunreachable
at the end of_start
. However, in my case, I need the wasm binary to be a "reactor": to export callbacks that are explicitly called by the runtime when needed. TheEXIT_RUNTIME
is supposed to do this, but then I get the following error:So, I tried to remove
main
and setIGNORE_MISSING_MAIN
but then I get the error described above.em++ --version
: