This is an AOT binary translator that converts Linux apps to executable binaries for other environments (i.e., Linux/ELF → WebAssembly, ...).
elfconv focuses primarily on WebAssembly (Wasm) conversion, offering many benefits.
- Without source codes
- Because you can port already-built applications directly, elfconv can generate Wasm even when the source code isn’t available. Furthermore, there’s no need to modify the source or rebuild the entire build environment to target Wasm.
- System-call compatibility layer
- Existing Wasm system APIs (WASI, Emscripten, etc.) aren’t compatible with Linux syscalls and cover only a small subset (e.g., about 45 in WASI preview 1 versus roughly 400 in current Linux). elfconv emulates Linux syscalls in the Wasm environment, allowing unmodified Linux applications to run.
- No dependence on language-community support
- Until now, generating Wasm required each language’s compiler to fully support Wasm output—demanding ongoing contributions from every language community. elfconv removes that requirement, so no per-language community support is needed.
In the past, there have been many Wasm porting projects (e.g., PostgreSQL on Wasm, LibreOffice on Wasm), but they were all large-scale undertakings.
- AOT Compilation
- There are existing projects that port Linux applications to Wasm by using CPU emulators such as v86 and container2wasm. However, these suffer from very poor performance—often slowing execution by tens of times or more. In contrast, elfconv uses ahead-of-time compilation, so its performance degradation is much smaller (please see the Benchmark below for details).
Warning
"elfconv is WORK IN PROGRESS" and the test is insufficient, so you may fail to compile your ELF binary or execute the generated Wasm binary. Current limitations are as follows. However, we will resolve these issues in the future.
- Only support for AArch64 ELF binary as an input binary
- No support for shared objects
- Only a part of the Linux system calls are implemented (ref:
runtime/syscalls/
)
For several test programs, we measured the performance of the WebAssembly (both for browser and WASI runtimes) generated by elfconv. The comparison target was the Wasm program compiled directly by Emscripten or WASI-SDK from the source code. elfconv converts the ELF/aarch64 produced by an existing compiler (gcc or clang) from the same source code into a Wasm program. In this performance test, we compared the performance of both Wasm programs.
Blog: elfconv: Linux Apps to High-Performance Wasm Binary Translator
Target Program | from source code | elfconv |
---|---|---|
eratosthenes_sieve (↓ better) |
0.567 (s) (100%) | 0.692 (s) (82%) |
LINPACK benchmark (↑ better) |
1617 (MFLOPS) (100%) | 1256 (MFLOPS) (78%) |
mnist-neural-network-plain-c (during 30 steps) (↑ better) |
2.138 (s) (100%) | 2.255 (s) (96%) |
Target Program | from source code | elfconv |
---|---|---|
eratosthenes_sieve (↓ better) |
0.362 (s) (100%) | 0.608 (s) (60%) |
LINPACK benchmark (↑ better) |
4821 (MFLOPS) (100%) | 2720 (MFLOPS) (56%) |
mnist-neural-network-plain-c (during 30 steps) (↑ better) |
2.271 (s) (100%) | 2.302 (s) (96%) |
As this table shows, Wasm on both Browser and WASI runtimes can be executed with less performance degradation than when Wasm is generated from the source code.
You can try elfconv using the Docker container (x86-64 and ARM64) by executing the commands as follows, and you can execute the Wasm application in both browser and host environment (WASI runtimes).
$ git clone --recursive https://github.com/yomaytk/elfconv
$ cd elfconv
elfconv/$ docker build . -t <image-name>
elfconv/$ docker run -it --rm -p 8080:8080 --name <container-name> <image-name>
### running build and test ...
# You can test elfconv using `bin/elfconv.sh`
~/elfconv# cd bin
~/elfconv/bin# TARGET=aarch64-wasm ./elfconv.sh /path/to/ELF # e.g., ../examples/hello/c/a.aarch64
# exe.js and exe.wasm should be generated.
~/elfconv/bin# emrun --no_browser --port 8080 exe.html
Web server root directory: /root/elfconv/bin
Now listening at http://0.0.0.0:8080/
Now, the Wasm application server has started, so that you can access it (e.g., http://localhost:8080/exe.wasm.html) from outside the container.
$ git clone --recursive https://github.com/yomaytk/elfconv
$ cd elfconv
$ docker build . -t <image-name>
$ docker run -it --name <container-name> <image-name>
### running build and test ...
# You can test elfconv using `bin/elfconv.sh`
~/elfconv# cd bin
~/elfconv/bin# TARGET=aarch64-wasi32 ./elfconv.sh /path/to/ELF # e.g. ../examples/hello/c/a.aarch64
~/elfconv/bin# wasmedge exe.wasm # wasmedge is preinstalled
elfconv provides the Dev Container environment using the root Dockerfile
and .devcontainer.json
, so you can develop without making the build environment if you can use Dev Container on your vscode (Please refer to the official website of vscode for basically using Dev Container).
The libraries required for the build are almost the same as those for Remill, and the main libraries are as follows. The other required libraries are automatically installed using cxx-common.
Name | Version |
---|---|
CMake | 3.14+ |
Google Flags | Latest |
Google Log | Latest |
Google Test | Latest |
LLVM | 16+ |
Clang | 16+ |
Intel XED | Latest |
Unzip | Latest |
ccache | Latest |
libelf | Latest |
libbfd | Latest |
libdwarf | Latest |
If you prepare these libraries, you can easily build elfconv by executing scripts/build.sh
as follows.
$ git clone --recursive https://github.com/yomaytk/elfconv
$ cd elfconv
/elfconv$ ./scripts/build.sh
Note
If you fail to build elfconv, please feel free to submit an issue!
After finishing the build, you can find the directory elfconv/build/
, and you can build the 'lifter' ('lifter' is the module that converts the ELF binary to LLVM bitcode and those source codes are mainly located in the backend/remill/
and lifter/
) by ninja after modifying the 'lifter' codes.
You can compile the ELF binary to the Wasm binary using scripts/dev.sh
as follows. dev.sh
execute the translation (ELF -> LLVM bitcode by 'lifter') and compiles the runtime/
(statically linked with generated LLVM bitcode) and generate the Wasm binary. When you execute the script, you should explicitly specify the path of the elfconv directory (/root/elfconv
on the container) with NEW_ROOT
or rewrite the ROOT_DIR
in dev.sh
.
# TARGET=<elf_arch>-<target_arch> (e.g., ELF/aarch64 -> Wasm for wasi32: aarch64-wasi32)
### Native
~/elfconv/build# NEW_ROOT=/path/to/elfconv TARGET=aarch64-native ../scripts/dev.sh path/to/ELF # generate the Native binary (Host architecture) under the elfconv/build/lifter
~/elfconv/build# ./exe.${HOST_CPU}
------------------------
### Browser (use xterm-pty (https://github.com/mame/xterm-pty))
~/elfconv/build# NEW_ROOT=/path/to/elfconv TARGET=aarch64-wasm ../scripts/dev.sh path/to/ELF # generate the Wasm binary under the elfconv/build/lifter
~/elfconv/build# emrun --no_browser --port 8080 ../examples/browser/exe.html # execute the generated Wasm binary with emscripten
------------------------
### Host (WASI Runtimes)
~/elfconv/build# NEW_ROOT=/path/to/elfconv TARGET=aarch64-wasi32 ../scripts/dev.sh path/to/ELF
~/elfconv/build# wasmedge ./exe.wasm # or wasmedge ./exe_o3.wasm
elfconv uses or references some projects as follows. Great thanks to all its developers!
- Remill (Apache Lisence 2.0)
- Original Source: https://github.com/lifting-bits/remill
- elfconv uses Remill to convert machine codes to LLVM IR instructions. The source code is contained in
./backend/remill
and is modified for using from front-end and supporting additional instructions.
- MyAOT (Apache Lisence 2.0)
- Original Source: https://github.com/AkihiroSuda/myaot
- An experimental AOT-ish compiler (Linux/riscv32 ELF → Linux/x86_64 ELF, Mach-O, Wasm, ...)
- elfconv has started as the successor project of MyAOT.