├── .gitignore ├── Makefile ├── README.md ├── basics ├── wasmception.c ├── wasmception.h └── wasmimp.js ├── example ├── Makefile ├── README.md ├── index.html ├── main.c └── main.syms ├── list_debug_sources.py ├── patches └── musl.1.patch └── wasm_standalone.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | src/ 3 | dist/ 4 | sysroot/ 5 | .vscode/ 6 | wasmception-*-bin.tar.gz 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Any copyright is dedicated to the Public Domain. 2 | # http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | ROOT_DIR=${CURDIR} 5 | LLVM_PROJECT_URL=https://github.com/llvm/llvm-project.git 6 | MUSL_PROJECT_URL=https://github.com/jfbastien/musl.git 7 | 8 | #LLVM_PROJECT_BRANCH=release/8.x 9 | LLVM_PROJECT_SHA=2ed0e79bb8efc3d642e3d8212e17a160f5ebb499 10 | MUSL_SHA=16d3d3825b4bd125244e43826fb0f0da79a1a4ad 11 | 12 | VERSION=0.2 13 | DEBUG_PREFIX_MAP=-fdebug-prefix-map=$(ROOT_DIR)=wasmception://v$(VERSION) 14 | WASM_TRIPLE=wasm32-unknown-unknown-wasm 15 | LLVM_VERSION=9 16 | #DEFAULT_SYSROOT_CFG=-DDEFAULT_SYSROOT=$(ROOT_DIR)/sysroot 17 | 18 | default: build 19 | ifdef DEFAULT_SYSROOT_CFG 20 | echo "Use $(DEBUG_PREFIX_MAP)" 21 | else 22 | echo "Use --sysroot=$(ROOT_DIR)/sysroot $(DEBUG_PREFIX_MAP)" 23 | endif 24 | 25 | clean: 26 | rm -rf build src dist sysroot wasmception-*-bin.tar.gz 27 | 28 | src/llvm-project.CLONED: 29 | mkdir -p src/ 30 | cd src/; git clone $(LLVM_PROJECT_URL) 31 | ifdef LLVM_PROJECT_BRANCH 32 | cd src/llvm-project; git checkout $(LLVM_PROJECT_BRANCH) 33 | else 34 | ifdef LLVM_PROJECT_SHA 35 | cd src/llvm-project; git checkout $(LLVM_PROJECT_SHA) 36 | endif 37 | endif 38 | touch src/llvm-project.CLONED 39 | 40 | src/musl.CLONED: 41 | mkdir -p src/ 42 | cd src/; git clone $(MUSL_PROJECT_URL) 43 | ifdef MUSL_SHA 44 | cd src/musl; git checkout $(MUSL_SHA) 45 | endif 46 | cd src/musl; patch -p 1 < $(ROOT_DIR)/patches/musl.1.patch 47 | touch src/musl.CLONED 48 | 49 | build/llvm.BUILT: src/llvm-project.CLONED 50 | mkdir -p build/llvm 51 | cd build/llvm; cmake -G "Unix Makefiles" \ 52 | -DCMAKE_BUILD_TYPE=MinSizeRel \ 53 | -DCMAKE_INSTALL_PREFIX=$(ROOT_DIR)/dist \ 54 | -DLLVM_TARGETS_TO_BUILD=WebAssembly \ 55 | -DLLVM_DEFAULT_TARGET_TRIPLE=$(WASM_TRIPLE) \ 56 | $(DEFAULT_SYSROOT_CFG) \ 57 | -DLLVM_EXTERNAL_CLANG_SOURCE_DIR=$(ROOT_DIR)/src/llvm-project/clang \ 58 | -DLLVM_EXTERNAL_LLD_SOURCE_DIR=$(ROOT_DIR)/src/llvm-project/lld \ 59 | -DLLVM_ENABLE_PROJECTS="lld;clang" \ 60 | $(ROOT_DIR)/src/llvm-project/llvm 61 | cd build/llvm; $(MAKE) -j 8 \ 62 | install-clang \ 63 | install-lld \ 64 | install-llc \ 65 | install-llvm-ar \ 66 | install-llvm-ranlib \ 67 | install-llvm-dwarfdump \ 68 | install-clang-headers \ 69 | install-llvm-nm \ 70 | install-llvm-size \ 71 | llvm-config 72 | touch build/llvm.BUILT 73 | 74 | build/musl.BUILT: src/musl.CLONED build/llvm.BUILT 75 | mkdir -p build/musl 76 | cd build/musl; $(ROOT_DIR)/src/musl/configure \ 77 | CC=$(ROOT_DIR)/dist/bin/clang \ 78 | CFLAGS="-O3 $(DEBUG_PREFIX_MAP)" \ 79 | --prefix=$(ROOT_DIR)/sysroot \ 80 | --enable-debug \ 81 | wasm32 82 | make -C build/musl -j 8 install CROSS_COMPILE=$(ROOT_DIR)/dist/bin/llvm- 83 | cp src/musl/arch/wasm32/libc.imports sysroot/lib/ 84 | touch build/musl.BUILT 85 | 86 | build/compiler-rt.BUILT: src/llvm-project.CLONED build/llvm.BUILT 87 | mkdir -p build/compiler-rt 88 | cd build/compiler-rt; cmake -G "Unix Makefiles" \ 89 | -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 90 | -DCMAKE_TOOLCHAIN_FILE=$(ROOT_DIR)/wasm_standalone.cmake \ 91 | -DCOMPILER_RT_BAREMETAL_BUILD=On \ 92 | -DCOMPILER_RT_BUILD_XRAY=OFF \ 93 | -DCOMPILER_RT_INCLUDE_TESTS=OFF \ 94 | -DCOMPILER_RT_ENABLE_IOS=OFF \ 95 | -DCOMPILER_RT_DEFAULT_TARGET_ONLY=On \ 96 | -DCMAKE_C_FLAGS="-O1 $(DEBUG_PREFIX_MAP)" \ 97 | -DLLVM_CONFIG_PATH=$(ROOT_DIR)/build/llvm/bin/llvm-config \ 98 | -DCOMPILER_RT_OS_DIR=. \ 99 | -DCMAKE_INSTALL_PREFIX=$(ROOT_DIR)/dist/lib/clang/$(LLVM_VERSION).0.0/ \ 100 | -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ 101 | $(ROOT_DIR)/src/llvm-project/compiler-rt/lib/builtins 102 | cd build/compiler-rt; make -j 8 install 103 | cp -R $(ROOT_DIR)/build/llvm/lib/clang $(ROOT_DIR)/dist/lib/ 104 | touch build/compiler-rt.BUILT 105 | 106 | build/libcxx.BUILT: src/llvm-project.CLONED build/llvm.BUILT build/compiler-rt.BUILT build/musl.BUILT 107 | mkdir -p build/libcxx 108 | cd build/libcxx; cmake -G "Unix Makefiles" \ 109 | -DCMAKE_TOOLCHAIN_FILE=$(ROOT_DIR)/wasm_standalone.cmake \ 110 | -DLLVM_CONFIG_PATH=$(ROOT_DIR)/build/llvm/bin/llvm-config \ 111 | -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ 112 | -DLIBCXX_ENABLE_THREADS:BOOL=OFF \ 113 | -DCMAKE_BUILD_TYPE=RelWithDebugInfo \ 114 | -DLIBCXX_ENABLE_SHARED:BOOL=OFF \ 115 | -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY:BOOL=OFF \ 116 | -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=OFF \ 117 | -DLIBCXX_CXX_ABI=libcxxabi \ 118 | -DLIBCXX_CXX_ABI_INCLUDE_PATHS=$(ROOT_DIR)/src/llvm-project/libcxxabi/include \ 119 | -DCMAKE_C_FLAGS="$(DEBUG_PREFIX_MAP)" \ 120 | -DCMAKE_CXX_FLAGS="$(DEBUG_PREFIX_MAP) -D_LIBCPP_HAS_MUSL_LIBC" \ 121 | --debug-trycompile \ 122 | $(ROOT_DIR)/src/llvm-project/libcxx 123 | cd build/libcxx; make -j 8 install 124 | touch build/libcxx.BUILT 125 | 126 | build/libcxxabi.BUILT: src/llvm-project.CLONED build/libcxx.BUILT build/llvm.BUILT 127 | mkdir -p build/libcxxabi 128 | cd build/libcxxabi; cmake -G "Unix Makefiles" \ 129 | -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ 130 | -DCMAKE_CXX_COMPILER_WORKS=ON \ 131 | -DCMAKE_C_COMPILER_WORKS=ON \ 132 | -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=OFF \ 133 | -DLIBCXXABI_ENABLE_SHARED:BOOL=OFF \ 134 | -DLIBCXXABI_ENABLE_THREADS:BOOL=OFF \ 135 | -DCXX_SUPPORTS_CXX11=ON \ 136 | -DLLVM_COMPILER_CHECKED=ON \ 137 | -DCMAKE_BUILD_TYPE=RelWithDebugInfo \ 138 | -DLIBCXXABI_LIBCXX_PATH=$(ROOT_DIR)/src/llvm-project/libcxx \ 139 | -DLIBCXXABI_LIBCXX_INCLUDES=$(ROOT_DIR)/sysroot/include/c++/v1 \ 140 | -DLLVM_CONFIG_PATH=$(ROOT_DIR)/build/llvm/bin/llvm-config \ 141 | -DCMAKE_TOOLCHAIN_FILE=$(ROOT_DIR)/wasm_standalone.cmake \ 142 | -DCMAKE_C_FLAGS="$(DEBUG_PREFIX_MAP)" \ 143 | -DCMAKE_CXX_FLAGS="$(DEBUG_PREFIX_MAP) -D_LIBCPP_HAS_MUSL_LIBC" \ 144 | -DUNIX:BOOL=ON \ 145 | --debug-trycompile \ 146 | $(ROOT_DIR)/src/llvm-project/libcxxabi 147 | cd build/libcxxabi; make -j 8 install 148 | touch build/libcxxabi.BUILT 149 | 150 | BASICS=sysroot/include/wasmception.h sysroot/lib/wasmception.wasm 151 | 152 | sysroot/include/wasmception.h: basics/wasmception.h 153 | cp basics/wasmception.h sysroot/include/ 154 | 155 | sysroot/lib/wasmception.wasm: build/llvm.BUILT basics/wasmception.c 156 | dist/bin/clang \ 157 | --sysroot=./sysroot basics/wasmception.c \ 158 | -c -O3 -g $(DEBUG_PREFIX_MAP) \ 159 | -o sysroot/lib/wasmception.wasm 160 | 161 | build: build/llvm.BUILT build/musl.BUILT build/compiler-rt.BUILT build/libcxxabi.BUILT build/libcxx.BUILT $(BASICS) 162 | 163 | strip: build/llvm.BUILT 164 | cd dist/bin; strip clang-$(LLVM_VERSION) llc lld llvm-ar 165 | 166 | collect-sources: 167 | -rm -rf build/sources build/sources.txt 168 | { find sysroot -name "*.o"; find sysroot -name "*.wasm"; find dist/lib sysroot -name "lib*.a"; } | \ 169 | xargs ./list_debug_sources.py | sort > build/sources.txt 170 | echo "sysroot/include" >> build/sources.txt 171 | for f in $$(cat build/sources.txt); \ 172 | do mkdir -p `dirname build/sources/$$f`; cp -R $$f `dirname build/sources/$$f`; done; 173 | cd build/sources && { git init; git checkout --orphan v$(VERSION); git add -A .; git commit -m "Sources"; } 174 | echo "cd build/sources && git push -f git@github.com:yurydelendik/wasmception.git v$(VERSION)" 175 | 176 | revisions: 177 | cd src/llvm-project; echo "LLVM_PROJECT_SHA=`git log -1 --format="%H"`" 178 | cd src/musl; echo "MUSL_SHA=`git log -1 --format="%H"`" 179 | 180 | OS_NAME=$(shell uname -s | tr '[:upper:]' '[:lower:]') 181 | pack: 182 | tar czf wasmception-${OS_NAME}-bin.tar.gz dist sysroot 183 | 184 | .PHONY: default clean build strip revisions pack collect-sources 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: this project is archived in favor of [WASI SDK](https://github.com/CraneStation/wasi-sdk/). 2 | 3 | # wasmception 4 | 5 | Minimal toolset for building wasm files 6 | 7 | ## Export functions 8 | 9 | Use linker's `--export` parameter to specify exports (with clang use `-Wl,--export`, e.g. `-Wl,--export=foo,--export=bar`). The use of `__attribute__ ((visibility ("default")))` is no longer preferable way to make methods visible -- `--export-dynamic` needs to be added. 10 | 11 | ## Compile C file 12 | 13 | ``` 14 | $(WASMCEPTION)/dist/bin/clang --sysroot=$(WASMCEPTION)/sysroot/ hi.c -o hi.wasm -nostartfiles -Wl,--no-entry,--export=foo 15 | ``` 16 | 17 | ## Compile C++ file 18 | 19 | ``` 20 | $(WASMCEPTION)/dist/bin/clang++ --sysroot=$(WASMCEPTION)/sysroot/ hi.cpp -o hi.wasm -nostartfiles -Wl,--no-entry,--export=bar -fno-exceptions 21 | ``` 22 | 23 | ## Required `main` and `_start` functions 24 | 25 | The `-nostartfiles` will not require you to define the `main` function, but will be looking for the `_start` function: 26 | use `-Wl,--no-entry` clang (linker) option to avoid specified entry point. As alternative, you can add `void _start() {}` 27 | (or `extern "C" void _start() { }` in C++) to make linker happy due to `-nostartfiles`. 28 | -------------------------------------------------------------------------------- /basics/wasmception.c: -------------------------------------------------------------------------------- 1 | void _start() {} -------------------------------------------------------------------------------- /basics/wasmception.h: -------------------------------------------------------------------------------- 1 | #ifndef __WASMCEPTION_H 2 | #define __WASMCEPTION_H 3 | 4 | #define WASM_EXPORT __attribute__ ((visibility ("default"))) 5 | 6 | #endif // __WASMCEPTION_H 7 | -------------------------------------------------------------------------------- /basics/wasmimp.js: -------------------------------------------------------------------------------- 1 | function syscall(stub, n) { 2 | switch (n) { 3 | default: 4 | console.error("NYI syscall", arguments); 5 | throw new Error("NYI syscall"); 6 | break; 7 | 8 | 9 | case /*brk*/ 45: 10 | return 0; 11 | case /*mmap2*/ 192: 12 | var instance = stub.instance; 13 | var memory = instance.exports.memory; 14 | var requested = arguments[3]; 15 | if (!stub.memory) { 16 | stub.memory = { 17 | object: memory, 18 | currentPosition: memory.buffer.byteLength, 19 | }; 20 | } 21 | var cur = stub.memory.currentPosition; 22 | if (cur + requested > memory.buffer.byteLength) { 23 | var need = Math.ceil((cur + requested - memory.buffer.byteLength) / 65536); 24 | memory.grow(need); 25 | } 26 | stub.memory.currentPosition += requested; 27 | return cur; 28 | } 29 | } 30 | 31 | function createWasmceptionStub() { 32 | var imports = { 33 | env: { 34 | __syscall0: (n) => syscall(stub, n), 35 | __syscall1: (n,a) => syscall(stub, n, a), 36 | __syscall2: (n,a,b) => syscall(stub, n, a, b), 37 | __syscall3: (n,a,b,c) => syscall(stub, n, a, b, c), 38 | __syscall4: (n,a,b,c,d) => syscall(stub, n, a, b, c, d), 39 | __syscall5: (n,a,b,c,d,e) => syscall(stub, n, a, b, c, d, e), 40 | __syscall6: (n,a,b,c,d,e,f) => syscall(stub, n, a, b, c, d, e, f), 41 | }, 42 | }; 43 | var stub = { 44 | imports, 45 | instance: null, 46 | memory: null, 47 | }; 48 | return stub; 49 | } -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | # Any copyright is dedicated to the Public Domain. 2 | # http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | .PHONY: all 5 | 6 | all: 7 | ../dist/bin/clang \ 8 | --sysroot=../sysroot \ 9 | -g \ 10 | -O3 \ 11 | -o main.wasm \ 12 | -nostartfiles \ 13 | -Wl,--allow-undefined-file=main.syms,--import-memory,--import-table,--demangle,--no-entry,--no-threads \ 14 | -Wl,--export=do_something \ 15 | -fvisibility=hidden \ 16 | main.c 17 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # About this example 2 | 3 | Build LLVM with experimental WASM target. Then run the Makefile: `make`. 4 | 5 | 6 | Arguments used to build `main.wasm`: 7 | 8 | - `--sysroot=../sysroot`: gives the sysroot path (with libc + abi) 9 | - (`-g`): debug symbols 10 | - `-O3`: optimize, optimize, optimize ! 11 | - `-o main.wasm`: output file 12 | - `-nostartfiles`: tell to CLang we don't have any `main` function 13 | - `-fvisibility=hidden`: tell to CLang all symbols are hidden by default 14 | - `-Wl`: pass to wasm linker the following arguments: 15 | - `--allow-undefined-file=main.syms`: allow to "link" some undefined symbols, they will be imported from JS 16 | - `--import-memory`: let us define the `WebAssembly.Memory` object from JS 17 | - `--import-table`: we also want to define ourself the `WebAssembly.Table` object from JS 18 | - (`--strip-all`): remove useless symbols 19 | - (`--demangle`): useful if we build and export function in C++ 20 | - `--no-entry`: tell to the linker we don't have any entry point (`__start` function) 21 | - (`--no-threads`): linking with thread support doesn't work well on every arch 22 | 23 | 24 | Run any small HTTP server (e.g. `python -m SimpleHTTPServer`). 25 | 26 | ## License 27 | 28 | Any copyright is dedicated to the Public Domain. 29 | http://creativecommons.org/publicdomain/zero/1.0/ 30 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |
8 | 9 |