├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── emsdk_manifest.json ├── examples_c ├── echo.c ├── green.c ├── hello1.c ├── hello2.c ├── hello_owl │ ├── Makefile │ ├── assets │ │ └── owl.png │ └── hello_owl.c ├── hello_sdl.c ├── include │ └── editline │ │ └── readline.h └── triangle.c ├── examples_wam ├── colors.wam ├── console_curses.wam ├── hello.wam └── snake.wam ├── examples_wat ├── addTwo.wat ├── arith.wat ├── caca_rand.wat └── echo.wat ├── grub-base.cfg ├── platform.h ├── platform_fooboot.c ├── platform_libc.c ├── platform_readline.c ├── runtest.py ├── thunk.c ├── thunk.h ├── util.c ├── util.h ├── wa.c ├── wa.h ├── wac.c ├── wace.c ├── wace_emscripten.c ├── wace_emscripten.h ├── wace_fooboot.c ├── wace_fooboot.h ├── wasi.c ├── wasi.h ├── wasi_core.h └── wax.c /.dockerignore: -------------------------------------------------------------------------------- 1 | wabt-* 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.d 4 | wac 5 | wax 6 | wace 7 | *.mem_fs_files.s 8 | *.syms.c 9 | examples_c/hello_sdl 10 | examples_c/triangle 11 | examples_c/*.js 12 | examples_c/*.html 13 | */*.wasm 14 | */*.wat 15 | spec/ 16 | wabt/ 17 | 18 | boot.iso 19 | isodir/ 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM trzeci/emscripten:sdk-tag-1.38.15-64bit 2 | MAINTAINER Joel Martin 3 | 4 | RUN dpkg --add-architecture i386 && \ 5 | apt-get -y update && \ 6 | apt-get -y install git-core cmake g++ \ 7 | lib32gcc-6-dev libsdl2-dev:i386 libsdl2-image-dev:i386 libedit-dev:i386 8 | 9 | RUN git clone https://github.com/WebAssembly/binaryen/ && \ 10 | cd binaryen && \ 11 | cmake . && make && \ 12 | make install 13 | 14 | RUN git clone --recursive https://github.com/WebAssembly/wabt/ && \ 15 | cd wabt && \ 16 | make gcc-release && \ 17 | make install-gcc-release && \ 18 | cp /src/wabt/bin/* /usr/local/bin/ 19 | 20 | # Cache emscripten port of SDL2 21 | RUN echo 'BINARYEN_ROOT="/usr/local"' >> /root/.emscripten && \ 22 | echo 'RELOCATABLE=""' >> /root/.emscripten && \ 23 | echo "int main() {}" > /tmp/nop.c && \ 24 | emcc -s WASM=1 -s SIDE_MODULE=1 -O2 -s USE_SDL=2 /tmp/nop.c -o /tmp/nop.wasm && \ 25 | emcc --show-ports && \ 26 | rm /tmp/nop* 27 | 28 | #rm -r /root/.emscripten_cache* && \ 29 | 30 | # To make sure emcc registers a timestamp difference properly do this 31 | # as a separate run command 32 | RUN touch /root/.emscripten_sanity 33 | 34 | # Additional tools for building wac/wace and wace OS 35 | RUN apt-get -y install python3 nasm xorriso grub-common grub-pc-bin 36 | 37 | 38 | # TODO: combine with top install 39 | RUN apt-get -y install freeglut3-dev:i386 40 | 41 | RUN npm install -g @kanaka/wamp@1.0.7 42 | ENV PATH $PATH:/emsdk_portable/node/bin 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) Joel Martin 2 | 3 | The wac project is licensed under the MPL 2.0 (Mozilla Public License 4 | 2.0). The text of the MPL 2.0 license is included below and can be 5 | found at https://www.mozilla.org/MPL/2.0/ 6 | 7 | By default, wac and wace link with the BSD licensed editline library 8 | (http://www.thrysoee.dk/editline/). The wac/wace build process can be 9 | switched to link with the GPL license GNU readline library with the 10 | USE_READLINE=1 flag. 11 | 12 | 13 | Mozilla Public License Version 2.0 14 | ================================== 15 | 16 | 1. Definitions 17 | -------------- 18 | 19 | 1.1. "Contributor" 20 | means each individual or legal entity that creates, contributes to 21 | the creation of, or owns Covered Software. 22 | 23 | 1.2. "Contributor Version" 24 | means the combination of the Contributions of others (if any) used 25 | by a Contributor and that particular Contributor's Contribution. 26 | 27 | 1.3. "Contribution" 28 | means Covered Software of a particular Contributor. 29 | 30 | 1.4. "Covered Software" 31 | means Source Code Form to which the initial Contributor has attached 32 | the notice in Exhibit A, the Executable Form of such Source Code 33 | Form, and Modifications of such Source Code Form, in each case 34 | including portions thereof. 35 | 36 | 1.5. "Incompatible With Secondary Licenses" 37 | means 38 | 39 | (a) that the initial Contributor has attached the notice described 40 | in Exhibit B to the Covered Software; or 41 | 42 | (b) that the Covered Software was made available under the terms of 43 | version 1.1 or earlier of the License, but not also under the 44 | terms of a Secondary License. 45 | 46 | 1.6. "Executable Form" 47 | means any form of the work other than Source Code Form. 48 | 49 | 1.7. "Larger Work" 50 | means a work that combines Covered Software with other material, in 51 | a separate file or files, that is not Covered Software. 52 | 53 | 1.8. "License" 54 | means this document. 55 | 56 | 1.9. "Licensable" 57 | means having the right to grant, to the maximum extent possible, 58 | whether at the time of the initial grant or subsequently, any and 59 | all of the rights conveyed by this License. 60 | 61 | 1.10. "Modifications" 62 | means any of the following: 63 | 64 | (a) any file in Source Code Form that results from an addition to, 65 | deletion from, or modification of the contents of Covered 66 | Software; or 67 | 68 | (b) any new file in Source Code Form that contains any Covered 69 | Software. 70 | 71 | 1.11. "Patent Claims" of a Contributor 72 | means any patent claim(s), including without limitation, method, 73 | process, and apparatus claims, in any patent Licensable by such 74 | Contributor that would be infringed, but for the grant of the 75 | License, by the making, using, selling, offering for sale, having 76 | made, import, or transfer of either its Contributions or its 77 | Contributor Version. 78 | 79 | 1.12. "Secondary License" 80 | means either the GNU General Public License, Version 2.0, the GNU 81 | Lesser General Public License, Version 2.1, the GNU Affero General 82 | Public License, Version 3.0, or any later versions of those 83 | licenses. 84 | 85 | 1.13. "Source Code Form" 86 | means the form of the work preferred for making modifications. 87 | 88 | 1.14. "You" (or "Your") 89 | means an individual or a legal entity exercising rights under this 90 | License. For legal entities, "You" includes any entity that 91 | controls, is controlled by, or is under common control with You. For 92 | purposes of this definition, "control" means (a) the power, direct 93 | or indirect, to cause the direction or management of such entity, 94 | whether by contract or otherwise, or (b) ownership of more than 95 | fifty percent (50%) of the outstanding shares or beneficial 96 | ownership of such entity. 97 | 98 | 2. License Grants and Conditions 99 | -------------------------------- 100 | 101 | 2.1. Grants 102 | 103 | Each Contributor hereby grants You a world-wide, royalty-free, 104 | non-exclusive license: 105 | 106 | (a) under intellectual property rights (other than patent or trademark) 107 | Licensable by such Contributor to use, reproduce, make available, 108 | modify, display, perform, distribute, and otherwise exploit its 109 | Contributions, either on an unmodified basis, with Modifications, or 110 | as part of a Larger Work; and 111 | 112 | (b) under Patent Claims of such Contributor to make, use, sell, offer 113 | for sale, have made, import, and otherwise transfer either its 114 | Contributions or its Contributor Version. 115 | 116 | 2.2. Effective Date 117 | 118 | The licenses granted in Section 2.1 with respect to any Contribution 119 | become effective for each Contribution on the date the Contributor first 120 | distributes such Contribution. 121 | 122 | 2.3. Limitations on Grant Scope 123 | 124 | The licenses granted in this Section 2 are the only rights granted under 125 | this License. No additional rights or licenses will be implied from the 126 | distribution or licensing of Covered Software under this License. 127 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 128 | Contributor: 129 | 130 | (a) for any code that a Contributor has removed from Covered Software; 131 | or 132 | 133 | (b) for infringements caused by: (i) Your and any other third party's 134 | modifications of Covered Software, or (ii) the combination of its 135 | Contributions with other software (except as part of its Contributor 136 | Version); or 137 | 138 | (c) under Patent Claims infringed by Covered Software in the absence of 139 | its Contributions. 140 | 141 | This License does not grant any rights in the trademarks, service marks, 142 | or logos of any Contributor (except as may be necessary to comply with 143 | the notice requirements in Section 3.4). 144 | 145 | 2.4. Subsequent Licenses 146 | 147 | No Contributor makes additional grants as a result of Your choice to 148 | distribute the Covered Software under a subsequent version of this 149 | License (see Section 10.2) or under the terms of a Secondary License (if 150 | permitted under the terms of Section 3.3). 151 | 152 | 2.5. Representation 153 | 154 | Each Contributor represents that the Contributor believes its 155 | Contributions are its original creation(s) or it has sufficient rights 156 | to grant the rights to its Contributions conveyed by this License. 157 | 158 | 2.6. Fair Use 159 | 160 | This License is not intended to limit any rights You have under 161 | applicable copyright doctrines of fair use, fair dealing, or other 162 | equivalents. 163 | 164 | 2.7. Conditions 165 | 166 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 167 | in Section 2.1. 168 | 169 | 3. Responsibilities 170 | ------------------- 171 | 172 | 3.1. Distribution of Source Form 173 | 174 | All distribution of Covered Software in Source Code Form, including any 175 | Modifications that You create or to which You contribute, must be under 176 | the terms of this License. You must inform recipients that the Source 177 | Code Form of the Covered Software is governed by the terms of this 178 | License, and how they can obtain a copy of this License. You may not 179 | attempt to alter or restrict the recipients' rights in the Source Code 180 | Form. 181 | 182 | 3.2. Distribution of Executable Form 183 | 184 | If You distribute Covered Software in Executable Form then: 185 | 186 | (a) such Covered Software must also be made available in Source Code 187 | Form, as described in Section 3.1, and You must inform recipients of 188 | the Executable Form how they can obtain a copy of such Source Code 189 | Form by reasonable means in a timely manner, at a charge no more 190 | than the cost of distribution to the recipient; and 191 | 192 | (b) You may distribute such Executable Form under the terms of this 193 | License, or sublicense it under different terms, provided that the 194 | license for the Executable Form does not attempt to limit or alter 195 | the recipients' rights in the Source Code Form under this License. 196 | 197 | 3.3. Distribution of a Larger Work 198 | 199 | You may create and distribute a Larger Work under terms of Your choice, 200 | provided that You also comply with the requirements of this License for 201 | the Covered Software. If the Larger Work is a combination of Covered 202 | Software with a work governed by one or more Secondary Licenses, and the 203 | Covered Software is not Incompatible With Secondary Licenses, this 204 | License permits You to additionally distribute such Covered Software 205 | under the terms of such Secondary License(s), so that the recipient of 206 | the Larger Work may, at their option, further distribute the Covered 207 | Software under the terms of either this License or such Secondary 208 | License(s). 209 | 210 | 3.4. Notices 211 | 212 | You may not remove or alter the substance of any license notices 213 | (including copyright notices, patent notices, disclaimers of warranty, 214 | or limitations of liability) contained within the Source Code Form of 215 | the Covered Software, except that You may alter any license notices to 216 | the extent required to remedy known factual inaccuracies. 217 | 218 | 3.5. Application of Additional Terms 219 | 220 | You may choose to offer, and to charge a fee for, warranty, support, 221 | indemnity or liability obligations to one or more recipients of Covered 222 | Software. However, You may do so only on Your own behalf, and not on 223 | behalf of any Contributor. You must make it absolutely clear that any 224 | such warranty, support, indemnity, or liability obligation is offered by 225 | You alone, and You hereby agree to indemnify every Contributor for any 226 | liability incurred by such Contributor as a result of warranty, support, 227 | indemnity or liability terms You offer. You may include additional 228 | disclaimers of warranty and limitations of liability specific to any 229 | jurisdiction. 230 | 231 | 4. Inability to Comply Due to Statute or Regulation 232 | --------------------------------------------------- 233 | 234 | If it is impossible for You to comply with any of the terms of this 235 | License with respect to some or all of the Covered Software due to 236 | statute, judicial order, or regulation then You must: (a) comply with 237 | the terms of this License to the maximum extent possible; and (b) 238 | describe the limitations and the code they affect. Such description must 239 | be placed in a text file included with all distributions of the Covered 240 | Software under this License. Except to the extent prohibited by statute 241 | or regulation, such description must be sufficiently detailed for a 242 | recipient of ordinary skill to be able to understand it. 243 | 244 | 5. Termination 245 | -------------- 246 | 247 | 5.1. The rights granted under this License will terminate automatically 248 | if You fail to comply with any of its terms. However, if You become 249 | compliant, then the rights granted under this License from a particular 250 | Contributor are reinstated (a) provisionally, unless and until such 251 | Contributor explicitly and finally terminates Your grants, and (b) on an 252 | ongoing basis, if such Contributor fails to notify You of the 253 | non-compliance by some reasonable means prior to 60 days after You have 254 | come back into compliance. Moreover, Your grants from a particular 255 | Contributor are reinstated on an ongoing basis if such Contributor 256 | notifies You of the non-compliance by some reasonable means, this is the 257 | first time You have received notice of non-compliance with this License 258 | from such Contributor, and You become compliant prior to 30 days after 259 | Your receipt of the notice. 260 | 261 | 5.2. If You initiate litigation against any entity by asserting a patent 262 | infringement claim (excluding declaratory judgment actions, 263 | counter-claims, and cross-claims) alleging that a Contributor Version 264 | directly or indirectly infringes any patent, then the rights granted to 265 | You by any and all Contributors for the Covered Software under Section 266 | 2.1 of this License shall terminate. 267 | 268 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 269 | end user license agreements (excluding distributors and resellers) which 270 | have been validly granted by You or Your distributors under this License 271 | prior to termination shall survive termination. 272 | 273 | ************************************************************************ 274 | * * 275 | * 6. Disclaimer of Warranty * 276 | * ------------------------- * 277 | * * 278 | * Covered Software is provided under this License on an "as is" * 279 | * basis, without warranty of any kind, either expressed, implied, or * 280 | * statutory, including, without limitation, warranties that the * 281 | * Covered Software is free of defects, merchantable, fit for a * 282 | * particular purpose or non-infringing. The entire risk as to the * 283 | * quality and performance of the Covered Software is with You. * 284 | * Should any Covered Software prove defective in any respect, You * 285 | * (not any Contributor) assume the cost of any necessary servicing, * 286 | * repair, or correction. This disclaimer of warranty constitutes an * 287 | * essential part of this License. No use of any Covered Software is * 288 | * authorized under this License except under this disclaimer. * 289 | * * 290 | ************************************************************************ 291 | 292 | ************************************************************************ 293 | * * 294 | * 7. Limitation of Liability * 295 | * -------------------------- * 296 | * * 297 | * Under no circumstances and under no legal theory, whether tort * 298 | * (including negligence), contract, or otherwise, shall any * 299 | * Contributor, or anyone who distributes Covered Software as * 300 | * permitted above, be liable to You for any direct, indirect, * 301 | * special, incidental, or consequential damages of any character * 302 | * including, without limitation, damages for lost profits, loss of * 303 | * goodwill, work stoppage, computer failure or malfunction, or any * 304 | * and all other commercial damages or losses, even if such party * 305 | * shall have been informed of the possibility of such damages. This * 306 | * limitation of liability shall not apply to liability for death or * 307 | * personal injury resulting from such party's negligence to the * 308 | * extent applicable law prohibits such limitation. Some * 309 | * jurisdictions do not allow the exclusion or limitation of * 310 | * incidental or consequential damages, so this exclusion and * 311 | * limitation may not apply to You. * 312 | * * 313 | ************************************************************************ 314 | 315 | 8. Litigation 316 | ------------- 317 | 318 | Any litigation relating to this License may be brought only in the 319 | courts of a jurisdiction where the defendant maintains its principal 320 | place of business and such litigation shall be governed by laws of that 321 | jurisdiction, without reference to its conflict-of-law provisions. 322 | Nothing in this Section shall prevent a party's ability to bring 323 | cross-claims or counter-claims. 324 | 325 | 9. Miscellaneous 326 | ---------------- 327 | 328 | This License represents the complete agreement concerning the subject 329 | matter hereof. If any provision of this License is held to be 330 | unenforceable, such provision shall be reformed only to the extent 331 | necessary to make it enforceable. Any law or regulation which provides 332 | that the language of a contract shall be construed against the drafter 333 | shall not be used to construe this License against a Contributor. 334 | 335 | 10. Versions of the License 336 | --------------------------- 337 | 338 | 10.1. New Versions 339 | 340 | Mozilla Foundation is the license steward. Except as provided in Section 341 | 10.3, no one other than the license steward has the right to modify or 342 | publish new versions of this License. Each version will be given a 343 | distinguishing version number. 344 | 345 | 10.2. Effect of New Versions 346 | 347 | You may distribute the Covered Software under the terms of the version 348 | of the License under which You originally received the Covered Software, 349 | or under the terms of any subsequent version published by the license 350 | steward. 351 | 352 | 10.3. Modified Versions 353 | 354 | If you create software not governed by this License, and you want to 355 | create a new license for such software, you may create and use a 356 | modified version of this License if you rename the license and remove 357 | any references to the name of the license steward (except to note that 358 | such modified license differs from this License). 359 | 360 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 361 | Licenses 362 | 363 | If You choose to distribute Source Code Form that is Incompatible With 364 | Secondary Licenses under the terms of this version of the License, the 365 | notice described in Exhibit B of this License must be attached. 366 | 367 | Exhibit A - Source Code Form License Notice 368 | ------------------------------------------- 369 | 370 | This Source Code Form is subject to the terms of the Mozilla Public 371 | License, v. 2.0. If a copy of the MPL was not distributed with this 372 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 373 | 374 | If it is not possible or desirable to put the notice in a particular 375 | file, then You may include the notice in a location (such as a LICENSE 376 | file in a relevant directory) where a recipient would be likely to look 377 | for such a notice. 378 | 379 | You may add additional accurate notices of copyright ownership. 380 | 381 | Exhibit B - "Incompatible With Secondary Licenses" Notice 382 | --------------------------------------------------------- 383 | 384 | This Source Code Form is "Incompatible With Secondary Licenses", as 385 | defined by the Mozilla Public License, v. 2.0. 386 | 387 | 388 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # User configurable build options 3 | 4 | # libc or fooboot 5 | PLATFORM = libc 6 | 7 | # WARNING: GPL license implications from using READLINE 8 | USE_READLINE ?= 9 | 10 | USE_SDL ?= 1 11 | 12 | #CFLAGS ?= -O2 -Wall -Werror -Wextra -MMD -MP 13 | CFLAGS ?= -O2 -Wall -Werror -MMD -MP 14 | 15 | EXTRA_WAC_LIBS ?= 16 | EXTRA_WACE_LIBS ?= 17 | 18 | 19 | ########################################################## 20 | 21 | CC = gcc $(CFLAGS) -std=gnu99 -m32 -g 22 | EMCC = emcc $(CFLAGS) -s WASM=1 -s SIDE_MODULE=1 -s LEGALIZE_JS_FFI=0 23 | 24 | WA_DEPS = util.o thunk.o 25 | 26 | ifeq (libc,$(PLATFORM)) 27 | CFLAGS += -DPLATFORM=1 28 | ifeq (,$(strip $(USE_READLINE))) 29 | RL_LIBRARY ?= edit 30 | else 31 | RL_LIBRARY ?= readline 32 | CFLAGS += -DUSE_READLINE=1 33 | endif 34 | WAC_LIBS = m dl $(RL_LIBRARY) 35 | WAX_LIBS = m dl 36 | WACE_LIBS = m dl $(RL_LIBRARY) 37 | ifneq (,$(strip $(USE_SDL))) 38 | WACE_LIBS += SDL2 SDL2_image GL glut 39 | endif 40 | else 41 | ifeq (fooboot,$(PLATFORM)) 42 | CFLAGS += -DPLATFORM=2 43 | else 44 | $(error unknown PLATFORM: $(PLATFORM)) 45 | endif 46 | endif 47 | 48 | WAC_LIBS += $(EXTRA_WAC_LIBS) 49 | WAX_LIBS += $(EXTRA_WAX_LIBS) 50 | WACE_LIBS += $(EXTRA_WACE_LIBS) 51 | 52 | # Basic build rules 53 | .PHONY: 54 | all: wac wax wace 55 | 56 | %.a: %.o 57 | ar rcs $@ $^ 58 | 59 | %.o: %.c 60 | $(CC) -c $(filter %.c,$^) -o $@ 61 | 62 | # Additional dependencies 63 | util.o: util.h 64 | wa.o: wa.h util.h platform.h 65 | thunk.o: wa.h thunk.h 66 | wa.a: util.o thunk.o platform_$(PLATFORM).o 67 | wac: wa.a wac.o 68 | wax: wa.a wasi.o wax.o 69 | wace: wa.a wace.o 70 | 71 | # 72 | # Platform 73 | # 74 | ifeq (libc,$(PLATFORM)) # libc Platform 75 | wac: platform_readline.o 76 | $(CC) -rdynamic -Wl,--no-as-needed -o $@ \ 77 | -Wl,--start-group $^ -Wl,--end-group $(foreach l,$(WAC_LIBS),-l$(l)) \ 78 | -Wl,--wrap=readline 79 | wax: 80 | $(CC) -rdynamic -Wl,--no-as-needed -o $@ \ 81 | -Wl,--start-group $^ -Wl,--end-group $(foreach l,$(WAX_LIBS),-l$(l)) 82 | wace: wace_emscripten.o platform_readline.o 83 | $(CC) -rdynamic -Wl,--no-as-needed -o $@ \ 84 | -Wl,--start-group $^ -Wl,--end-group $(foreach l,$(WACE_LIBS),-l$(l)) \ 85 | -Wl,--wrap=readline 86 | 87 | else # fooboot OS platform 88 | 89 | FOO_TARGETS = wac wax wace 90 | include fooboot/Makefile 91 | 92 | wace: wace_fooboot.o 93 | endif 94 | 95 | 96 | .PHONY: 97 | clean:: 98 | rm -f *.o *.a *.d wac wax wace wace-sdl.c \ 99 | lib/*.o lib/*.d kernel/*.o kernel/*.d \ 100 | examples_c/*.js examples_c/*.html \ 101 | examples_c/*.wasm examples_c/*.wat \ 102 | examples_wat/*.wasm 103 | 104 | ########################################################## 105 | 106 | # Wat example build rules 107 | examples_wam/%.wasm: examples_wam/printnum.wam examples_wam/%.wam 108 | wamp $^ > $(subst .wasm,.wat,$@) 109 | wasm-as $(subst .wasm,.wat,$@) -o $@ 110 | 111 | examples_wam/hello.wasm: examples_wam/console_curses.wam 112 | examples_wam/colors.wasm: examples_wam/console_curses.wam 113 | examples_wam/snake.wasm: examples_wam/console_curses.wam 114 | 115 | examples_wat/%.wasm: examples_wat/%.wat 116 | wasm-as $< -o $@ 117 | 118 | # General C example build rules 119 | examples_c/%.wasm: examples_c/%.c 120 | $(EMCC) -I examples_c/include -s USE_SDL=2 $< -o $@ 121 | 122 | .SECONDARY: 123 | examples_c/%.wat: examples_c/%.wasm 124 | wasm-dis $< -o $@ 125 | 126 | examples_c/%: examples_c/%.c 127 | $(CC) $< -o $@ -lSDL2 -lSDL2_image -lGL -lglut 128 | 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wac - WebAssembly in C 2 | 3 | A Minimal WebAssembly interpreter written in C. Supports the 4 | WebAssembly MVP (minimum viable product) version of the WebAssembly 5 | specification. 6 | 7 | There are three different builds of wac: 8 | 9 | * **wac**: (WebAssembly in C) Minimal client with an interactive REPL 10 | mode. Designed to run standalone wasm files compiled with 11 | `wat2wasm` or `wasm-as`. Passes most spec tests apart from some 12 | multi-module import/export tests. 13 | * **wax**: (WebAssembly in C with WASI) Client with WebAssembly System 14 | Interface APIs (WASI). 15 | * **wace**: (WebAssembly in C with Emscripten) Client with host 16 | library/memory integration. Designed to run wasm code that has been 17 | built with Emscripten (using `-s SIDE_MODULE=1 -s LEGALIZE_JS_FFI=0`). 18 | 19 | ## Prerequisites 20 | 21 | To build wac/wax/wace you need a 32-bit version of gcc and 32-bit 22 | versions of SDL2 and libedit. On 64-bit Ubuntu/Debian these can be 23 | installed like this: 24 | 25 | ``` 26 | dpkg --add-architecture i386 27 | apt-get update 28 | apt-get install lib32gcc-4.9-dev libSDL2-dev:i386 libedit-dev:i386 29 | ``` 30 | 31 | To compile wat source files to binary wasm modules you will need the 32 | `wasm-as` program from [Binaryen](https://github.com/WebAssembly/binaryen). 33 | To compile C programs to wasm modules you will need Binaryen and 34 | [emscripten](https://github.com/kanaka/emscripten). 35 | 36 | As an alternative to downloading and building the above tools, the 37 | `kanaka/wac` docker image (1.7GB) has 32-bit gcc compiler/libraries, 38 | emscripten, and binaryen preinstalled. The docker image can be started 39 | with appropriate file mounts like this: 40 | 41 | ``` 42 | docker run -v `pwd`:/wac -w /wac -it kanaka/wac bash 43 | ``` 44 | 45 | All the build commands below can be run within the docker container. 46 | 47 | 48 | ## wac usage 49 | 50 | Build wac: 51 | 52 | ```bash 53 | $ make wac 54 | ``` 55 | 56 | Use `wasm-as` to compile a simple wat program to a wasm: 57 | 58 | ```bash 59 | $ make examples_wat/arith.wasm 60 | ``` 61 | 62 | Now load the compiled wasm file and invoke some functions: 63 | 64 | ```bash 65 | $ ./wac examples_wat/arith.wasm add 2 3 66 | 0x5:i32 67 | $ ./wac examples_wat/arith.wasm mul 7 8 68 | 0x38:i32 69 | ``` 70 | 71 | wac also supports a very simple REPL (read-eval-print-loop) mode that 72 | runs commands in the form of `FUNC ARG...`: 73 | 74 | ``` 75 | $ ./wac --repl examples_wat/arith.wasm 76 | > sub 10 5 77 | 0x5:i32 78 | > div 13 4 79 | 0x3:i32 80 | ``` 81 | 82 | ## wax usage 83 | 84 | Build wax: 85 | 86 | ```bash 87 | $ make wax 88 | ``` 89 | 90 | Use `wasm-as` to compile a wat program that uses the WASI interface: 91 | 92 | ```bash 93 | $ make examples_wat/echo.wasm 94 | ``` 95 | 96 | Now run the compiled wasm file. The program reads text from stdin and 97 | echos it to stdout until an EOF (Ctrl-D) is sent. 98 | 99 | ```bash 100 | $ ./wax examples_wat/echo.wasm 101 | > foo 102 | foo 103 | > bar 104 | bar 105 | > 106 | ``` 107 | 108 | 109 | ## wace usage 110 | 111 | Build wace: 112 | 113 | ```bash 114 | $ make wace 115 | ``` 116 | 117 | Use emscripten/binaryen to compile some simple C programs and run them 118 | using wace: 119 | 120 | ```bash 121 | $ make examples_c/hello1.wasm 122 | $ ./wace examples_c/hello1.wasm 123 | hello world 124 | 125 | $ make examples_c/hello2.wasm 126 | $ ./wace examples_c/hello2.wasm 127 | hello malloc people 128 | ``` 129 | 130 | Use emscripten/binaryen to compile some C SDL programs and run them 131 | using wace: 132 | 133 | ```bash 134 | $ make examples_c/hello_sdl.wasm 135 | $ ./wace examples_c/hello_sdl.wasm 136 | INFO: OpenGL shaders: ENABLED 137 | INFO: Created renderer: opengl 138 | # Blue Window displayed for 2 seconds 139 | Done. 140 | 141 | $ make examples_c/triangle.wasm 142 | $ ./wace examples_c/triangle.wasm 143 | # A colorfully shaded triangle is rendered 144 | ``` 145 | 146 | ## Running WebAssembly spec tests 147 | 148 | wac includes a `runtest.py` test driver which can be used for running 149 | tests from the WebAssembly specification. 150 | 151 | Check out the spec: 152 | 153 | ``` 154 | git clone https://github.com/WebAssembly/spec 155 | ``` 156 | 157 | You will need `wat2wasm` to compile the spec tests. Check-out and 158 | build [wabt](https://github.com/WebAssembly/wabt) (wabbit): 159 | 160 | ``` 161 | git clone --recursive https://github.com/WebAssembly/wabt 162 | make -C wabt gcc-release 163 | ``` 164 | 165 | Run the `func.wast` test file (to test function calls) from the spec: 166 | 167 | ``` 168 | ./runtest.py --wat2wasm ./wabt/out/gcc/Release/wat2wasm --interpreter ./wac spec/test/core/func.wast 169 | ``` 170 | 171 | Run all the spec tests apart from a few that currently fail (mostly due to 172 | `runtest.py` missing support for some syntax used in those test files): 173 | 174 | ``` 175 | BROKE_TESTS="comments exports imports linking names data elem inline-module" 176 | for t in $(ls spec/test/core/*.wast | grep -Fv "${BROKE_TESTS// /$'\n'}"); do 177 | echo -e "\nTESTING ${t}" 178 | ./runtest.py ${t} || break 179 | done 180 | ``` 181 | 182 | 183 | ## Standalone Builds using Fooboot 184 | 185 | wac and wace can be built to run as standalone bootable programs 186 | using [fooboot](https://github.com/kanaka/fooboot): 187 | 188 | ``` 189 | cd wac 190 | git clone https://github.com/kanaka/fooboot 191 | make PLATFORM=fooboot clean 192 | make PLATFORM=fooboot wac wace examples_wat/addTwo.wasm 193 | ``` 194 | 195 | The `fooboot/runfoo` script can be used to boot wac/wace with QEMU. 196 | `fooboot/runfoo` also creates a connection on a serial port (COM2) 197 | that allows files to be read from the host system: 198 | 199 | ``` 200 | fooboot/runfoo wac --repl examples_wat/addTwo.wasm 201 | QEMU waiting for connection on: disconnected:tcp:localhost:21118,server 202 | webassembly> addTwo 2 3 203 | 0x5:i32 204 | ``` 205 | 206 | The standalone wac/wace builds can also be built into an ISO image 207 | that can boot directly on real hardware. You will need Grub 2 and the 208 | Grub PC/BIOS binary files (grub-pc-bin) and the xorriso program to be 209 | able to do this. Also, the wasm modules that you wish to run must be 210 | built into the binary to become part of a simple in-memory 211 | file-system: 212 | 213 | ``` 214 | echo "examples_wat/addTwo.wasm" > mem_fs_files 215 | make PLATFORM=fooboot \ 216 | FOO_TARGETS="wac" \ 217 | FOO_CMDLINE="examples_wat/addTwo.wasm addTwo 3 4" \ 218 | boot.iso 219 | ``` 220 | 221 | You can now boot the ISO with QEMU like this: 222 | 223 | ``` 224 | qemu-system-i386 -cdrom boot.iso 225 | ``` 226 | 227 | Or you can burn the ISO to a USB device and boot from it on real 228 | hardware. This will destroy any data on the USB device! Also, make 229 | completely sure that /dev/MY\_USB\_DEVICE is really the USB device you 230 | want to overwrite and not your hard drive. You have been warned! 231 | 232 | ``` 233 | sudo dd if=boot.iso of=/dev/MY_USB_DEVICE && sync 234 | # Now boot you can boot from the USB device 235 | ``` 236 | 237 | ## License 238 | 239 | MPL-2.0 (see LICENSE). 240 | -------------------------------------------------------------------------------- /emsdk_manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": [ 3 | { 4 | "id": "clang", 5 | "version": "incoming", 6 | "bitness": 64, 7 | "install_path": "clang/fastcomp", 8 | "git_branch": "incoming", 9 | "url": "https://github.com/kripken/emscripten-fastcomp/", 10 | "clang_url": "https://github.com/kripken/emscripten-fastcomp-clang/", 11 | "custom_install_script": "build_fastcomp", 12 | "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", 13 | "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", 14 | "cmake_build_type": "RelWithDebInfo" 15 | }, 16 | { 17 | "id": "node", 18 | "version": "8.0.0-nightly20170427892ce06dbd", 19 | "bitness": 64, 20 | "windows_url": "https://nodejs.org/download/nightly/v8.0.0-nightly20170427892ce06dbd/node-v8.0.0-nightly20170427892ce06dbd-win-x64.zip", 21 | "linux_url": "https://nodejs.org/download/nightly/v8.0.0-nightly20170427892ce06dbd/node-v8.0.0-nightly20170427892ce06dbd-linux-x64.tar.gz", 22 | "activated_path": "%installation_dir%/bin", 23 | "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'" 24 | }, 25 | { 26 | "id": "emscripten", 27 | "version": "kanaka", 28 | "bitness": 64, 29 | "append_bitness": false, 30 | "url": "https://github.com/kanaka/emscripten/", 31 | "git_branch": "incoming", 32 | "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", 33 | "activated_path": "%installation_dir%", 34 | "activated_env": "EMSCRIPTEN=%installation_dir%", 35 | "cmake_build_type": "RelWithDebInfo", 36 | "custom_install_script": "build_optimizer", 37 | "custom_is_installed_script": "is_optimizer_installed", 38 | "custom_uninstall_script": "uninstall_optimizer" 39 | }, 40 | { 41 | "id": "binaryen", 42 | "version": "master", 43 | "bitness": 64, 44 | "append_bitness": false, 45 | "url": "https://github.com/WebAssembly/binaryen/", 46 | "git_branch": "master", 47 | "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", 48 | "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", 49 | "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", 50 | "cmake_build_type": "Release", 51 | "custom_install_script": "build_binaryen", 52 | "custom_is_installed_script": "is_binaryen_installed", 53 | "custom_uninstall_script": "uninstall_binaryen" 54 | } 55 | ], 56 | 57 | "sdks": [ 58 | { 59 | "version": "kanaka", 60 | "bitness": 64, 61 | "uses": ["clang-incoming-64bit", "binaryen-master-64bit", "node-8.0.0-nightly20170427892ce06dbd-64bit", "emscripten-kanaka-64bit"], 62 | "os": "linux" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /examples_c/echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main() 8 | { 9 | for(;;) { 10 | char *line; 11 | line = readline("echo> "); 12 | if (!line) return 0; // EOF 13 | add_history(line); // Add input to history. 14 | 15 | printf("%s\n", line); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples_c/green.c: -------------------------------------------------------------------------------- 1 | // From: https://rosettacode.org/wiki/OpenGL#C 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | glutInit(&argc, argv); 12 | glutInitWindowSize(640, 480); 13 | glutCreateWindow("Green"); 14 | 15 | glClearColor(0.3,0.5,0.3,0.0); 16 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 17 | glFlush(); 18 | 19 | sleep(2); 20 | 21 | return EXIT_SUCCESS; 22 | } 23 | -------------------------------------------------------------------------------- /examples_c/hello1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main () { 4 | printf("hello world\n"); 5 | return 1; 6 | } 7 | -------------------------------------------------------------------------------- /examples_c/hello2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main () { 6 | char *name; 7 | name = malloc(20); 8 | strncpy(name, "malloc people", 14); 9 | printf("hello %s\n", name); 10 | return 2; 11 | } 12 | -------------------------------------------------------------------------------- /examples_c/hello_owl/Makefile: -------------------------------------------------------------------------------- 1 | CC = emcc 2 | all: hello_owl.c 3 | $(CC) hello_owl.c -O2 -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS='["png"]' --preload-file assets -o hello_owl.html 4 | -------------------------------------------------------------------------------- /examples_c/hello_owl/assets/owl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kanaka/wac/8c9da26b4fee6d1a320f944e3a55cc37d5ff1e09/examples_c/hello_owl/assets/owl.png -------------------------------------------------------------------------------- /examples_c/hello_owl/hello_owl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __EMSCRIPTEN__ 8 | #include 9 | #endif 10 | 11 | /** 12 | * Loads the image located at 'fileName' and copies it to the 13 | * renderer 'renderer' 14 | */ 15 | int testImage(SDL_Renderer* renderer, const char* fileName) 16 | { 17 | SDL_Surface *image = IMG_Load(fileName); 18 | if (!image) 19 | { 20 | return 0; 21 | } 22 | int result = image->w; 23 | 24 | /** 25 | * position and size that you wish the image to be copied 26 | * to on the renderer: 27 | */ 28 | SDL_Rect dest = {.x = 200, .y = 100, .w = 200, .h = 200}; 29 | 30 | SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, image); 31 | 32 | SDL_RenderCopy (renderer, tex, NULL, &dest); 33 | 34 | /** 35 | * Now that the image data is in the renderer, we can free the memory 36 | * used by the texture and the image surface 37 | */ 38 | SDL_DestroyTexture (tex); 39 | 40 | SDL_FreeSurface (image); 41 | 42 | return result; 43 | } 44 | 45 | int main() 46 | { 47 | SDL_Init(SDL_INIT_VIDEO); 48 | 49 | SDL_Window *window; 50 | SDL_Renderer *renderer; 51 | 52 | SDL_CreateWindowAndRenderer(600, 400, 0, &window, &renderer); 53 | 54 | int result = 0; 55 | 56 | /** 57 | * Set up a white background 58 | */ 59 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 60 | SDL_RenderClear(renderer); 61 | 62 | /** 63 | * Load and copy the test image to the renderer 64 | */ 65 | result = testImage(renderer, "assets/owl.png"); 66 | if (result == 0) { 67 | result = testImage(renderer, "examples_c/hello_owl/assets/owl.png"); 68 | } 69 | if (result == 0) { 70 | printf("IMG_Load: %s\n", IMG_GetError()); 71 | return 0; 72 | } 73 | 74 | /** 75 | * Show what is in the renderer 76 | */ 77 | SDL_RenderPresent(renderer); 78 | 79 | printf("you should see an image.\n"); 80 | 81 | SDL_Delay(2000); 82 | 83 | return 0; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /examples_c/hello_sdl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //#include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG); 10 | SDL_Init(SDL_INIT_VIDEO); 11 | 12 | SDL_Window *window; 13 | SDL_Renderer *renderer; 14 | 15 | SDL_CreateWindowAndRenderer(320, 200, 0, &window, &renderer); 16 | 17 | /** 18 | * Set up a blue background 19 | */ 20 | SDL_SetRenderDrawColor(renderer, 128, 128, 255, 255); 21 | SDL_RenderClear(renderer); 22 | 23 | /** 24 | * Show what is in the renderer 25 | */ 26 | SDL_RenderPresent(renderer); 27 | 28 | SDL_Delay(2000); 29 | 30 | printf("Done.\n"); 31 | 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /examples_c/include/editline/readline.h: -------------------------------------------------------------------------------- 1 | /* $NetBSD: readline.h,v 1.34 2013/05/28 00:10:34 christos Exp $ */ 2 | 3 | /*- 4 | * Copyright (c) 1997 The NetBSD Foundation, Inc. 5 | * All rights reserved. 6 | * 7 | * This code is derived from software contributed to The NetBSD Foundation 8 | * by Jaromir Dolecek. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | #ifndef _READLINE_H_ 32 | #define _READLINE_H_ 33 | 34 | #include 35 | #include 36 | 37 | /* list of readline stuff supported by editline library's readline wrapper */ 38 | 39 | /* typedefs */ 40 | typedef int Function(const char *, int); 41 | typedef void VFunction(void); 42 | typedef void VCPFunction(char *); 43 | typedef char *CPFunction(const char *, int); 44 | typedef char **CPPFunction(const char *, int, int); 45 | typedef char *rl_compentry_func_t(const char *, int); 46 | typedef int rl_command_func_t(int, int); 47 | 48 | /* only supports length */ 49 | typedef struct { 50 | int length; 51 | } HISTORY_STATE; 52 | 53 | typedef void *histdata_t; 54 | 55 | typedef struct _hist_entry { 56 | const char *line; 57 | histdata_t data; 58 | } HIST_ENTRY; 59 | 60 | typedef struct _keymap_entry { 61 | char type; 62 | #define ISFUNC 0 63 | #define ISKMAP 1 64 | #define ISMACR 2 65 | Function *function; 66 | } KEYMAP_ENTRY; 67 | 68 | #define KEYMAP_SIZE 256 69 | 70 | typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE]; 71 | typedef KEYMAP_ENTRY *Keymap; 72 | 73 | #define control_character_threshold 0x20 74 | #define control_character_bit 0x40 75 | 76 | #ifndef CTRL 77 | #include 78 | #if !defined(__sun) && !defined(__hpux) && !defined(_AIX) 79 | #include 80 | #endif 81 | #ifndef CTRL 82 | #define CTRL(c) ((c) & 037) 83 | #endif 84 | #endif 85 | #ifndef UNCTRL 86 | #define UNCTRL(c) (((c) - 'a' + 'A')|control_character_bit) 87 | #endif 88 | 89 | #define RUBOUT 0x7f 90 | #define ABORT_CHAR CTRL('G') 91 | #define RL_READLINE_VERSION 0x0402 92 | #define RL_PROMPT_START_IGNORE '\1' 93 | #define RL_PROMPT_END_IGNORE '\2' 94 | 95 | /* global variables used by readline enabled applications */ 96 | #ifdef __cplusplus 97 | extern "C" { 98 | #endif 99 | extern const char *rl_library_version; 100 | extern int rl_readline_version; 101 | extern char *rl_readline_name; 102 | extern FILE *rl_instream; 103 | extern FILE *rl_outstream; 104 | extern char *rl_line_buffer; 105 | extern int rl_point, rl_end; 106 | extern int history_base, history_length; 107 | extern int max_input_history; 108 | extern char *rl_basic_word_break_characters; 109 | extern char *rl_completer_word_break_characters; 110 | extern char *rl_completer_quote_characters; 111 | extern Function *rl_completion_entry_function; 112 | extern char *(*rl_completion_word_break_hook)(void); 113 | extern CPPFunction *rl_attempted_completion_function; 114 | extern int rl_attempted_completion_over; 115 | extern int rl_completion_type; 116 | extern int rl_completion_query_items; 117 | extern char *rl_special_prefixes; 118 | extern int rl_completion_append_character; 119 | extern int rl_inhibit_completion; 120 | extern Function *rl_pre_input_hook; 121 | extern Function *rl_startup_hook; 122 | extern char *rl_terminal_name; 123 | extern int rl_already_prompted; 124 | extern char *rl_prompt; 125 | /* 126 | * The following is not implemented 127 | */ 128 | extern int rl_catch_signals; 129 | extern int rl_catch_sigwinch; 130 | extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, 131 | emacs_meta_keymap, 132 | emacs_ctlx_keymap; 133 | extern int rl_filename_completion_desired; 134 | extern int rl_ignore_completion_duplicates; 135 | extern int (*rl_getc_function)(FILE *); 136 | extern VFunction *rl_redisplay_function; 137 | extern VFunction *rl_completion_display_matches_hook; 138 | extern VFunction *rl_prep_term_function; 139 | extern VFunction *rl_deprep_term_function; 140 | extern int readline_echoing_p; 141 | extern int _rl_print_completions_horizontally; 142 | 143 | /* supported functions */ 144 | char *readline(const char *); 145 | int rl_initialize(void); 146 | 147 | void using_history(void); 148 | int add_history(const char *); 149 | void clear_history(void); 150 | void stifle_history(int); 151 | int unstifle_history(void); 152 | int history_is_stifled(void); 153 | int where_history(void); 154 | HIST_ENTRY *current_history(void); 155 | HIST_ENTRY *history_get(int); 156 | HIST_ENTRY *remove_history(int); 157 | HIST_ENTRY *replace_history_entry(int, const char *, histdata_t); 158 | int history_total_bytes(void); 159 | int history_set_pos(int); 160 | HIST_ENTRY *previous_history(void); 161 | HIST_ENTRY *next_history(void); 162 | int history_search(const char *, int); 163 | int history_search_prefix(const char *, int); 164 | int history_search_pos(const char *, int, int); 165 | int read_history(const char *); 166 | int write_history(const char *); 167 | int history_truncate_file (const char *, int); 168 | int history_expand(char *, char **); 169 | char **history_tokenize(const char *); 170 | const char *get_history_event(const char *, int *, int); 171 | char *history_arg_extract(int, int, const char *); 172 | 173 | char *tilde_expand(char *); 174 | char *filename_completion_function(const char *, int); 175 | char *username_completion_function(const char *, int); 176 | int rl_complete(int, int); 177 | int rl_read_key(void); 178 | char **completion_matches(const char *, CPFunction *); 179 | void rl_display_match_list(char **, int, int); 180 | 181 | int rl_insert(int, int); 182 | int rl_insert_text(const char *); 183 | void rl_reset_terminal(const char *); 184 | int rl_bind_key(int, rl_command_func_t *); 185 | int rl_newline(int, int); 186 | void rl_callback_read_char(void); 187 | void rl_callback_handler_install(const char *, VCPFunction *); 188 | void rl_callback_handler_remove(void); 189 | void rl_redisplay(void); 190 | int rl_get_previous_history(int, int); 191 | void rl_prep_terminal(int); 192 | void rl_deprep_terminal(void); 193 | int rl_read_init_file(const char *); 194 | int rl_parse_and_bind(const char *); 195 | int rl_variable_bind(const char *, const char *); 196 | void rl_stuff_char(int); 197 | int rl_add_defun(const char *, Function *, int); 198 | HISTORY_STATE *history_get_history_state(void); 199 | void rl_get_screen_size(int *, int *); 200 | void rl_set_screen_size(int, int); 201 | char *rl_filename_completion_function (const char *, int); 202 | int _rl_abort_internal(void); 203 | int _rl_qsort_string_compare(char **, char **); 204 | char **rl_completion_matches(const char *, rl_compentry_func_t *); 205 | void rl_forced_update_display(void); 206 | int rl_set_prompt(const char *); 207 | int rl_on_new_line(void); 208 | 209 | /* 210 | * The following are not implemented 211 | */ 212 | int rl_kill_text(int, int); 213 | Keymap rl_get_keymap(void); 214 | void rl_set_keymap(Keymap); 215 | Keymap rl_make_bare_keymap(void); 216 | int rl_generic_bind(int, const char *, const char *, Keymap); 217 | int rl_bind_key_in_map(int, rl_command_func_t *, Keymap); 218 | void rl_cleanup_after_signal(void); 219 | void rl_free_line_state(void); 220 | #ifdef __cplusplus 221 | } 222 | #endif 223 | 224 | #endif /* _READLINE_H_ */ 225 | -------------------------------------------------------------------------------- /examples_c/triangle.c: -------------------------------------------------------------------------------- 1 | // From: https://rosettacode.org/wiki/OpenGL#C 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void paint(void) 8 | { 9 | glClearColor(0.3,0.3,0.3,0.0); 10 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 11 | 12 | glShadeModel(GL_SMOOTH); 13 | 14 | glLoadIdentity(); 15 | glTranslatef(-15.0, -15.0, 0.0); 16 | 17 | glBegin(GL_TRIANGLES); 18 | glColor3f(1.0, 0.0, 0.0); 19 | glVertex2f(0.0, 0.0); 20 | glColor3f(0.0, 1.0, 0.0); 21 | glVertex2f(30.0, 0.0); 22 | glColor3f(0.0, 0.0, 1.0); 23 | glVertex2f(0.0, 30.0); 24 | glEnd(); 25 | 26 | glFlush(); 27 | } 28 | 29 | void reshape(int width, int height) 30 | { 31 | glViewport(0, 0, width, height); 32 | glMatrixMode(GL_PROJECTION); 33 | glLoadIdentity(); 34 | glOrtho(-30.0, 30.0, -30.0, 30.0, -30.0, 30.0); 35 | glMatrixMode(GL_MODELVIEW); 36 | } 37 | 38 | int main(int argc, char *argv[]) 39 | { 40 | glutInit(&argc, argv); 41 | glutInitWindowSize(640, 480); 42 | glutCreateWindow("Triangle"); 43 | 44 | glutDisplayFunc(paint); 45 | glutReshapeFunc(reshape); 46 | 47 | glutMainLoop(); 48 | 49 | return EXIT_SUCCESS; 50 | } 51 | -------------------------------------------------------------------------------- /examples_wam/colors.wam: -------------------------------------------------------------------------------- 1 | (module $colors 2 | 3 | (import "env" "memory" (memory 256)) 4 | (import "env" "memoryBase" (global $memoryBase i32)) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | (func $main 9 | (LET $i 0 10 | $nbuf (STATIC_ARRAY 10)) 11 | ($init_console) 12 | (loop $loop 13 | (drop ($itoa $i $nbuf)) 14 | ($draw $nbuf 1 $i 7) 15 | ($draw "-->" 4 $i 7) 16 | ($draw "X" 8 $i $i) 17 | (local.set $i (i32.add 1 $i)) 18 | (br_if $loop (i32.lt_s $i 16)) 19 | ) 20 | ($draw "'q' to quit" 10 20 2) 21 | (loop $loop 22 | (br_if $loop (i32.ne (global.get $KEY_Q) ($readch))) 23 | ) 24 | ($term_console) 25 | ) 26 | 27 | (export "_main" (func $main)) 28 | 29 | ) 30 | -------------------------------------------------------------------------------- /examples_wam/console_curses.wam: -------------------------------------------------------------------------------- 1 | (module $console_curses 2 | 3 | (import "env" "usleep" (func $usleep (param i32) (result i32))) 4 | 5 | ;;(import "env" "signal" (func)) 6 | ;;(import "env" "stdscr" (global $stdscr i32)) 7 | (import "env" "initscr" (func $initscr (result i32))) 8 | (import "env" "endwin" (func $endwin)) 9 | (import "env" "cbreak" (func $cbreak)) 10 | (import "env" "noecho" (func $noecho)) 11 | (import "env" "nodelay" (func $nodelay (param i32 i32))) 12 | (import "env" "keypad" (func $keypad (param i32 i32))) 13 | (import "env" "refresh" (func $refresh)) 14 | (import "env" "clear" (func $clear)) 15 | 16 | (import "env" "init_pair" (func $init_pair (param i32 i32 i32))) 17 | (import "env" "start_color" (func $start_color (param))) 18 | (import "env" "COLOR_PAIR" (func $COLOR_PAIR (param i32) (result i32))) 19 | (import "env" "curs_set" (func $curs_set (param i32))) 20 | (import "env" "attron" (func $attron (param i32))) 21 | (import "env" "attroff" (func $attroff (param i32))) 22 | 23 | (import "env" "getch" (func $getch (result i32))) 24 | (import "env" "mvaddch" (func $mvaddch (param i32 i32 i32))) 25 | (import "env" "mvaddstr" (func $mvaddstr (param i32 i32 i32))) 26 | (import "env" "addstr" (func $addstr (param i32))) 27 | 28 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | 30 | (global $COLOR_RED i32 1) 31 | (global $COLOR_GREEN i32 2) 32 | (global $COLOR_BROWN i32 3) 33 | (global $COLOR_BLUE i32 4) 34 | (global $COLOR_MAGENTA i32 5) 35 | (global $COLOR_CYAN i32 6) 36 | (global $COLOR_LGREY i32 7) 37 | 38 | (global $KEY_Q i32 (CHR "q")) 39 | (global $KEY_UP i32 259) 40 | (global $KEY_DOWN i32 258) 41 | (global $KEY_LEFT i32 260) 42 | (global $KEY_RIGHT i32 261) 43 | 44 | (func $init_color (param $idx i32 $fg i32 $bg i32) 45 | ($init_pair $idx $fg $bg) 46 | ) 47 | 48 | (func $init_console 49 | (LET $scr ($initscr) $i 1) 50 | ($clear) ;; clear the screen 51 | ($noecho) ;; no echo of typed characters 52 | ($cbreak) ;; do not wait for newline to send chars 53 | ($nodelay $scr 1) ;; return -1 instead of blocking on getch 54 | ($keypad $scr 1) ;; single code for special keys 55 | ($curs_set 0) ;; hide cursor 56 | ($start_color) ;; enable colors 57 | 58 | ;; Initialize first 15 colors 59 | (loop $loop 60 | ($init_color $i $i 0) 61 | (local.set $i (i32.add 1 $i)) 62 | (br_if $loop (i32.lt_s $i 16)) 63 | ) 64 | ) 65 | 66 | (func $term_console 67 | ($endwin) 68 | ) 69 | 70 | 71 | (func $print (param $str i32) 72 | ($addstr $str) 73 | ) 74 | 75 | (func $draw (param $str i32) (param $x i32) (param $y i32) (param $color i32) 76 | ($attron ($COLOR_PAIR $color)) 77 | ($mvaddstr $y $x $str) 78 | ($attroff ($COLOR_PAIR $color)) 79 | ) 80 | 81 | (func $drawch (param $ch i32) (param $x i32) (param $y i32) (param $color i32) 82 | ($attron ($COLOR_PAIR $color)) 83 | ($mvaddch $y $x $ch) 84 | ($attroff ($COLOR_PAIR $color)) 85 | ) 86 | 87 | (func $readch (result i32) 88 | ($getch) 89 | ) 90 | 91 | (export "init_console" (func $init_console)) 92 | (export "term_console" (func $term_console)) 93 | (export "print" (func $print)) 94 | (export "draw" (func $draw)) 95 | (export "drawch" (func $drawch)) 96 | (export "readch" (func $readch)) 97 | ) 98 | -------------------------------------------------------------------------------- /examples_wam/hello.wam: -------------------------------------------------------------------------------- 1 | (module $colors 2 | 3 | (import "env" "memory" (memory 256)) 4 | (import "env" "memoryBase" (global $memoryBase i32)) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | (func $main 9 | ($init_console) 10 | ($draw "hello world" 10 10 1) 11 | ($draw "'q' to quit" 10 12 2) 12 | (loop $loop 13 | (br_if $loop (i32.ne (global.get $KEY_Q) ($readch)))) 14 | ($term_console) 15 | ) 16 | 17 | (export "_main" (func $main)) 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /examples_wam/snake.wam: -------------------------------------------------------------------------------- 1 | (module $snake 2 | 3 | (import "env" "memory" (memory 256)) 4 | (import "env" "memoryBase" (global $memoryBase i32)) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | (global $SNAKE_ARRAY_LEN (mut i32) 300) 9 | 10 | (global $snake (mut i32) 0) 11 | (global $snake_len (mut i32) 1) 12 | (global $snake_dir (mut i32) 1) 13 | (global $snake_speed (mut i32) 128) 14 | 15 | (func $get_x (param $pos i32) (result i32) 16 | (i32.load8_u offset=0 (i32.add (global.get $snake) (i32.mul 3 $pos))) 17 | ) 18 | (func $get_y (param $pos i32) (result i32) 19 | (i32.load8_u offset=1 (i32.add (global.get $snake) (i32.mul 3 $pos))) 20 | ) 21 | (func $get_ch (param $pos i32) (result i32) 22 | (i32.load8_u offset=2 (i32.add (global.get $snake) (i32.mul 3 $pos))) 23 | ) 24 | (func $set_x_y_ch (param $pos i32) 25 | (param $x i32) (param $y i32) (param $ch i32) 26 | (LET $ptr (i32.add (global.get $snake) (i32.mul 3 $pos))) 27 | (if (i32.ne -1 $x) (i32.store8 offset=0 $ptr $x)) 28 | (if (i32.ne -1 $x) (i32.store8 offset=1 $ptr $y)) 29 | (i32.store8 offset=2 $ptr $ch) 30 | ) 31 | 32 | (func $draw_snake_head (param $x i32 $y i32) 33 | (LET $c 0 34 | $dir (global.get $snake_dir)) 35 | (local.set $c (if i32 (i32.eq 0 $dir) (CHR "^") 36 | (else (if i32 (i32.eq 1 $dir) (CHR ">") 37 | (else (if i32 (i32.eq 2 $dir) (CHR "v") 38 | (else (CHR "<")))))))) 39 | ($drawch $c $x $y (global.get $COLOR_GREEN)) 40 | ) 41 | 42 | (func $draw_snake 43 | (LET $cur 0 44 | $x 0 45 | $y 0 46 | $c 0) 47 | (loop $loop 48 | (local.set $x ($get_x $cur)) 49 | (local.set $y ($get_y $cur)) 50 | (local.set $c ($get_ch $cur)) 51 | ($drawch $c $x $y (global.get $COLOR_GREEN)) 52 | (local.set $cur (i32.add 1 $cur)) 53 | (br_if $loop (i32.lt_u $cur (global.get $snake_len))) 54 | ) 55 | ($draw_snake_head $x $y) 56 | ) 57 | 58 | (func $tick (param $grow i32) 59 | (LET $dir (global.get $snake_dir) 60 | $x1 ($get_x 0) 61 | $y1 ($get_y 0) 62 | $x2 ($get_x (i32.sub (global.get $snake_len) 1)) 63 | $y2 ($get_y (i32.sub (global.get $snake_len) 1)) 64 | $dy (if i32 (i32.eq 0 $dir) -1 (if i32 (i32.eq 2 $dir) 1 0)) 65 | $dx (if i32 (i32.eq 1 $dir) 1 (if i32 (i32.eq 3 $dir) -1 0)) 66 | $c (if i32 (i32.rem_u $dir 2) (CHR "-") (CHR "|")) 67 | $idx 0) 68 | 69 | ;; Add to the head (end) 70 | ($set_x_y_ch (global.get $snake_len) 71 | (i32.add $x2 $dx) (i32.add $y2 $dy) $c) 72 | (global.set $snake_len (i32.add 1 (global.get $snake_len))) 73 | 74 | ;; Erase the tail (start) if not growing 75 | (if (i32.eqz $grow) 76 | (then 77 | (loop $loop 78 | (i32.store8 79 | (i32.add $idx (global.get $snake)) 80 | (i32.load8_u (i32.add 3 (i32.add $idx (global.get $snake))))) 81 | (local.set $idx (i32.add 1 $idx)) 82 | (br_if $loop (i32.lt_u $idx (i32.mul 3 (global.get $snake_len)))) 83 | ) 84 | (global.set $snake_len (i32.sub (global.get $snake_len) 1)))) 85 | 86 | ($draw_snake) 87 | ) 88 | 89 | (func $main 90 | (LET $ch 0 91 | $pos 0 92 | $new_dir (global.get $snake_dir) 93 | $tick 0) 94 | (global.set $snake (STATIC_ARRAY 300)) 95 | (global.set $snake_speed 64) 96 | 97 | ($set_x_y_ch 0 9 10 (CHR "-")) 98 | ($set_x_y_ch 1 10 10 (CHR "-")) 99 | (global.set $snake_len 2) 100 | 101 | ($init_console) 102 | ($draw "SNAKE GAME" 35 23 (global.get $COLOR_CYAN)) 103 | 104 | ($draw_snake) 105 | ;;(loop $loop (br $loop)) 106 | 107 | (loop $loop 108 | (local.set $pos (i32.sub (global.get $snake_len) 1)) 109 | (local.set $tick (i32.add $tick 1)) 110 | (drop ($usleep 2000)) 111 | (if (i32.eqz (i32.rem_u $tick (global.get $snake_speed))) 112 | (then 113 | (global.set $snake_dir $new_dir) 114 | ($tick 0))) 115 | 116 | (local.set $ch ($readch)) 117 | (br_if $loop (i32.eq -1 $ch)) 118 | (if (i32.eq $ch (global.get $KEY_UP)) (local.set $new_dir 0)) 119 | (if (i32.eq $ch (global.get $KEY_DOWN)) (local.set $new_dir 2)) 120 | (if (i32.eq $ch (global.get $KEY_LEFT)) (local.set $new_dir 3)) 121 | (if (i32.eq $ch (global.get $KEY_RIGHT)) (local.set $new_dir 1)) 122 | (if (i32.rem_u (i32.add (global.get $snake_dir) $new_dir) 2) 123 | (then 124 | ;; Left or right turn 125 | ($set_x_y_ch $pos -1 -1 (CHR "+")) 126 | ($draw_snake_head ($get_x $pos) ($get_y $pos))) 127 | (else 128 | ;; 180 or turn towards current direction 129 | (local.set $new_dir (global.get $snake_dir)))) 130 | (br_if $loop (i32.ne $ch (global.get $KEY_Q))) 131 | ) 132 | 133 | ($term_console) 134 | ) 135 | 136 | (export "_main" (func $main)) 137 | ) 138 | -------------------------------------------------------------------------------- /examples_wat/addTwo.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $addTwo (param i32 i32) (result i32) 3 | (i32.add 4 | (local.get 0) 5 | (local.get 1))) 6 | (export "addTwo" (func $addTwo))) 7 | -------------------------------------------------------------------------------- /examples_wat/arith.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $add (param i32 i32) (result i32) 3 | (i32.add 4 | (local.get 0) 5 | (local.get 1))) 6 | (func $sub (param i32 i32) (result i32) 7 | (i32.sub 8 | (local.get 0) 9 | (local.get 1))) 10 | (func $mul (param i32 i32) (result i32) 11 | (i32.mul 12 | (local.get 0) 13 | (local.get 1))) 14 | (func $div (param i32 i32) (result i32) 15 | (i32.div_u 16 | (local.get 0) 17 | (local.get 1))) 18 | (export "add" (func $add)) 19 | (export "sub" (func $sub)) 20 | (export "mul" (func $mul)) 21 | (export "div" (func $div))) 22 | -------------------------------------------------------------------------------- /examples_wat/caca_rand.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "libcaca.so.0" "caca_rand" 3 | (func $caca_rand (param i32 i32) (result i32))) 4 | (func $doRand (param i32 i32) (result i32) 5 | (call $caca_rand 6 | (local.get 0) (local.get 1))) 7 | (export "rand" (func $doRand))) 8 | -------------------------------------------------------------------------------- /examples_wat/echo.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "wasi_unstable" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) 3 | (import "wasi_unstable" "fd_read" (func $fd_read (param i32 i32 i32 i32) (result i32))) 4 | 5 | (memory 10) 6 | (export "memory" (memory 0)) 7 | 8 | ;; prompt __wasi_ciovec_t struct 9 | (data (i32.const 0) "\08\00\00\00") ;; buf: pointer to prompt string 10 | (data (i32.const 4) "\02\00\00\00") ;; buf_len: 2 characters 11 | ;; string 12 | (data (i32.const 8) "> ") 13 | 14 | ;; read buf __wasi_ciovec_t struct 15 | (data (i32.const 16) "\18\00\00\00") ;; buf: pointer to string 16 | (data (i32.const 20) "\64\00\00\00") ;; buf_len: 100 characters max 17 | ;; string 18 | (data (i32.const 24) "\00") ;; buf (of 100 characters) to hold read in string 19 | 20 | (func $main 21 | (local $ret i32) 22 | (block $done 23 | (loop $loop 24 | (drop (call $fd_write (i32.const 1) (i32.const 0) (i32.const 1) (i32.const 256))) 25 | (local.set $ret (call $fd_read (i32.const 0) (i32.const 16) (i32.const 1) (i32.const 256))) 26 | (if (i32.eqz (i32.load (i32.const 256)) (i32.const 0)) (br $done)) 27 | (drop (call $fd_write (i32.const 0) (i32.const 16) (i32.const 1) (i32.const 256))) 28 | (br $loop) 29 | ) 30 | ) 31 | ) 32 | 33 | (export "_start" $main) 34 | ) 35 | -------------------------------------------------------------------------------- /grub-base.cfg: -------------------------------------------------------------------------------- 1 | set timeout=2 2 | -------------------------------------------------------------------------------- /platform.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_H 2 | #define FS_H 3 | 4 | #include 5 | 6 | void *acalloc(size_t nmemb, size_t size, char *name); 7 | void *arecalloc(void *ptr, size_t old_nmemb, size_t nmemb, 8 | size_t size, char *name); 9 | 10 | #if PLATFORM==1 11 | 12 | #ifdef USE_READLINE 13 | // WARNING: GPL license implications 14 | #include 15 | #include 16 | #else 17 | #include 18 | #endif 19 | uint8_t *mmap_file(char *path, int *len); 20 | 21 | #else 22 | 23 | bool readline(const char *prompt, char *buf, int cnt); 24 | 25 | #endif 26 | 27 | int read_file(char *path, char *buf); 28 | 29 | // Dynamic lib resolution 30 | bool resolvesym(char *filename, char *symbol, void **val, char **err); 31 | 32 | #endif // of FS_H 33 | -------------------------------------------------------------------------------- /platform_fooboot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "readline_buf.h" 14 | #include "util.h" 15 | 16 | extern uint8_t *memory; 17 | extern uint8_t *memoryBase; 18 | 19 | 20 | // Assert calloc 21 | void *acalloc(size_t nmemb, size_t size, char *name) { 22 | void *res = calloc(nmemb, size); 23 | if (res == NULL) { 24 | FATAL("Could not allocate %d bytes for %s", (int)(nmemb * size), name); 25 | } 26 | //printf("<<< acalloc res: %p\n", res); 27 | return res; 28 | } 29 | 30 | // Assert realloc/calloc 31 | void *arecalloc(void *ptr, size_t old_nmemb, size_t nmemb, 32 | size_t size, char *name) { 33 | void *res = calloc(nmemb, size); 34 | if (res == NULL) { 35 | FATAL("Could not allocate %d bytes for %s", (int)(nmemb * size), name); 36 | } 37 | memmove(res, ptr, old_nmemb * size); 38 | // Initialize new memory 39 | memset(res + old_nmemb * size, 0, (nmemb - old_nmemb) * size); 40 | //printf("<<< arecalloc res: %p\n", res); 41 | return res; 42 | } 43 | 44 | // 45 | // Some extra library routines 46 | // 47 | 48 | // Returns true if line successfully read into buf, false for EOF 49 | bool readline(const char *prompt, char *buf, int cnt) { 50 | return readline_buf(prompt, buf, cnt); 51 | } 52 | 53 | int printline(const char *s) { 54 | return fputs(s, stdout); 55 | } 56 | 57 | 58 | void add_history(char *line) { 59 | return; 60 | } 61 | 62 | //int _gettimeofday(timeval *tv, struct timezone *tz) { 63 | int _gettimeofday(void *tv, void *tz) { 64 | // TODO: implement 65 | return -1; 66 | } 67 | 68 | int read_file(char *path, char *buf) { 69 | return vfs_read_file(path, buf); 70 | } 71 | 72 | // 73 | // Dynamic lib resolution 74 | // 75 | 76 | bool resolvesym(char *filename, char *symbol, void **val, char **err) { 77 | //printf("resolvesym filename: '%s', symbol: '%s'\n", filename, symbol); 78 | if (filename && strcmp(filename, "env") != 0) { 79 | *err = "resolvesym with filename unimplmented"; 80 | return false; 81 | } 82 | *val = sym_table_lookup(symbol); 83 | //printf(" *val: %p\n", *val); 84 | return true; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /platform_libc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "util.h" 15 | 16 | // Assert calloc 17 | void *acalloc(size_t nmemb, size_t size, char *name) { 18 | void *res = calloc(nmemb, size); 19 | if (res == NULL) { 20 | FATAL("Could not allocate %ul bytes for %s", nmemb * size, name); 21 | } 22 | return res; 23 | } 24 | 25 | // Assert realloc/calloc 26 | void *arecalloc(void *ptr, size_t old_nmemb, size_t nmemb, 27 | size_t size, char *name) { 28 | void *res = realloc(ptr, nmemb * size); 29 | if (res == NULL) { 30 | FATAL("Could not allocate %ul bytes for %s", nmemb * size, name); 31 | } 32 | // Initialize new memory 33 | memset(res + old_nmemb * size, 0, (nmemb - old_nmemb) * size); 34 | return res; 35 | } 36 | 37 | // 38 | // Some extra lirary routines 39 | // 40 | 41 | int printline(const char *s) { 42 | return fputs(s, stdout); 43 | } 44 | 45 | int get_time_ms() { 46 | time_t current_time = time(NULL); // Get the current epoch time 47 | return (int32_t)current_time; 48 | } 49 | 50 | // open and mmap a file 51 | uint8_t *mmap_file(char *path, int *len) { 52 | int fd; 53 | int res; 54 | struct stat sb; 55 | uint8_t *bytes; 56 | 57 | fd = open(path, O_RDONLY); 58 | if (fd < 0) { FATAL("could not open file '%s'\n", path); } 59 | res = fstat(fd, &sb); 60 | if (res < 0) { FATAL("could not stat file '%s' (%d)\n", path, res); } 61 | 62 | bytes = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 63 | if (len) { 64 | *len = sb.st_size; // Return length if requested 65 | } 66 | if (bytes == MAP_FAILED) { FATAL("could not mmap file '%s'", path); } 67 | return bytes; 68 | } 69 | 70 | int read_file(char *path, char *buf) { 71 | int fd; 72 | struct stat sb; 73 | size_t len; 74 | 75 | fd = open(path, O_RDONLY); 76 | if (fd < 0) { 77 | printf("could not open file '%s'\n", path); 78 | return 0; 79 | } 80 | if (fstat(fd, &sb) < 0) { 81 | printf("could stat file '%s'\n", path); 82 | return 0; 83 | } 84 | 85 | len = read(fd, buf, sb.st_size); 86 | if (len < sb.st_size) { 87 | printf("failed to read all of '%s'\n", path); 88 | return 0; 89 | } 90 | 91 | return len; 92 | } 93 | 94 | // 95 | // Dynamic lib resolution 96 | // 97 | 98 | // If filename is NULL, a NULL handle will be used 99 | // Returns true if resolution successful 100 | // Return false and sets err if resolution is not successful 101 | bool resolvesym(char *filename, char *symbol, void **val, char **err) { 102 | void *handle = NULL; 103 | dlerror(); // clear errors 104 | //log("filename: %s, symbol: %s\n", filename, symbol); 105 | if (filename) { 106 | handle = dlopen(filename, RTLD_LAZY); 107 | if (!handle) { 108 | *err = dlerror(); 109 | return false; 110 | } 111 | } 112 | *val = dlsym(handle, symbol); 113 | //log(" val: 0x%p\n", *val); 114 | if ((*err = dlerror()) != NULL) { 115 | return false; 116 | } 117 | return true; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /platform_readline.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // 6 | // Some extra lirary routines 7 | // 8 | 9 | // Returns true if line successfully read into buf, false for EOF 10 | extern char * __real_readline (const char *prompt); 11 | bool __wrap_readline(const char *prompt, char *buf, int cnt) { 12 | // Use GNU Readline to get the input with prompt 13 | char *input = __real_readline(prompt); 14 | 15 | if (input == NULL) { 16 | return false; // EOF 17 | } 18 | 19 | // Copy it into buf and free input 20 | snprintf(buf, cnt, "%s", input); 21 | free(input); 22 | 23 | return true; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /runtest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | import os, sys, re 5 | import argparse, time 6 | import signal, atexit, tempfile, subprocess 7 | 8 | from subprocess import Popen, STDOUT, PIPE 9 | from select import select 10 | 11 | # Pseudo-TTY and terminal manipulation 12 | import pty, array, fcntl, termios 13 | 14 | IS_PY_3 = sys.version_info[0] == 3 15 | 16 | debug_file = None 17 | log_file = None 18 | 19 | def debug(data): 20 | if debug_file: 21 | debug_file.write(data) 22 | debug_file.flush() 23 | 24 | def log(data, end='\n'): 25 | if log_file: 26 | log_file.write(data + end) 27 | log_file.flush() 28 | print(data, end=end) 29 | sys.stdout.flush() 30 | 31 | # TODO: do we need to support '\n' too 32 | import platform 33 | if platform.system().find("CYGWIN_NT") >= 0: 34 | # TODO: this is weird, is this really right on Cygwin? 35 | sep = "\n\r\n" 36 | else: 37 | sep = "\r\n" 38 | rundir = None 39 | 40 | class Runner(): 41 | def __init__(self, args, no_pty=False): 42 | #print "args: %s" % repr(args) 43 | self.no_pty = no_pty 44 | 45 | # Cleanup child process on exit 46 | atexit.register(self.cleanup) 47 | 48 | self.p = None 49 | env = os.environ 50 | env['TERM'] = 'dumb' 51 | env['INPUTRC'] = '/dev/null' 52 | env['PERL_RL'] = 'false' 53 | if no_pty: 54 | self.p = Popen(args, bufsize=0, 55 | stdin=PIPE, stdout=PIPE, stderr=STDOUT, 56 | preexec_fn=os.setsid, 57 | env=env) 58 | self.stdin = self.p.stdin 59 | self.stdout = self.p.stdout 60 | else: 61 | # provide tty to get 'interactive' readline to work 62 | master, slave = pty.openpty() 63 | 64 | # Set terminal size large so that readline will not send 65 | # ANSI/VT escape codes when the lines are long. 66 | buf = array.array('h', [100, 200, 0, 0]) 67 | fcntl.ioctl(master, termios.TIOCSWINSZ, buf, True) 68 | 69 | self.p = Popen(args, bufsize=0, 70 | stdin=slave, stdout=slave, stderr=STDOUT, 71 | preexec_fn=os.setsid, 72 | env=env) 73 | # Now close slave so that we will get an exception from 74 | # read when the child exits early 75 | # http://stackoverflow.com/questions/11165521 76 | os.close(slave) 77 | self.stdin = os.fdopen(master, 'r+b', 0) 78 | self.stdout = self.stdin 79 | 80 | #print "started" 81 | self.buf = "" 82 | self.last_prompt = "" 83 | 84 | def read_to_prompt(self, prompts, timeout): 85 | end_time = time.time() + timeout 86 | while time.time() < end_time: 87 | [outs,_,_] = select([self.stdout], [], [], 1) 88 | if self.stdout in outs: 89 | new_data = self.stdout.read(1) 90 | new_data = new_data.decode("utf-8") if IS_PY_3 else new_data 91 | #print("new_data: '%s'" % new_data) 92 | debug(new_data) 93 | if self.no_pty: 94 | self.buf += new_data.replace("\n", "\r\n") 95 | else: 96 | self.buf += new_data 97 | self.buf = self.buf.replace("\r\r", "\r") 98 | for prompt in prompts: 99 | regexp = re.compile(prompt) 100 | match = regexp.search(self.buf) 101 | if match: 102 | end = match.end() 103 | buf = self.buf[0:end-len(prompt)] 104 | self.buf = self.buf[end:] 105 | self.last_prompt = prompt 106 | return buf 107 | return None 108 | 109 | def writeline(self, str): 110 | def _to_bytes(s): 111 | return bytes(s, "utf-8") if IS_PY_3 else s 112 | 113 | self.stdin.write(_to_bytes(str + "\n")) 114 | 115 | def cleanup(self): 116 | #print "cleaning up" 117 | if self.p: 118 | try: 119 | os.killpg(self.p.pid, signal.SIGTERM) 120 | except OSError: 121 | pass 122 | self.p = None 123 | 124 | def assert_prompt(runner, prompts, timeout): 125 | # Wait for the initial prompt 126 | header = runner.read_to_prompt(prompts, timeout=timeout) 127 | if not header == None: 128 | if header: 129 | log("Started with:\n%s" % header) 130 | else: 131 | log("Did not one of following prompt(s): %s" % repr(prompts)) 132 | log(" Got : %s" % repr(r.buf)) 133 | sys.exit(1) 134 | 135 | 136 | ### WebAssembly specific 137 | 138 | parser = argparse.ArgumentParser( 139 | description="Run a test file against a WebAssembly interpreter") 140 | parser.add_argument('--wat2wasm', type=str, 141 | default=os.environ.get("WAT2WASM", "wat2wasm"), 142 | help="Path to wat2wasm program") 143 | parser.add_argument('--interpreter', type=str, 144 | default=os.environ.get("WA_CMD", "./wac"), 145 | help="Path to WebAssembly interpreter") 146 | parser.add_argument('--verbose', action='store_true', 147 | help="Verbose logging") 148 | parser.add_argument('--no-cleanup', action='store_true', 149 | help="Keep temporary *.wasm files") 150 | 151 | parser.add_argument('--rundir', 152 | help="change to the directory before running tests") 153 | parser.add_argument('--start-timeout', default=10, type=int, 154 | help="default timeout for initial prompt") 155 | parser.add_argument('--test-timeout', default=20, type=int, 156 | help="default timeout for each individual test action") 157 | parser.add_argument('--no-pty', action='store_true', 158 | help="Use direct pipes instead of pseudo-tty") 159 | parser.add_argument('--log-file', type=str, 160 | help="Write messages to the named file in addition the screen") 161 | parser.add_argument('--debug-file', type=str, 162 | help="Write all test interaction the named file") 163 | 164 | parser.add_argument('test_file', type=argparse.FileType('r'), 165 | help="a WebAssembly *.wast test file") 166 | 167 | 168 | # regex patterns of tests to skip 169 | C_SKIP_TESTS = ( 170 | # names.wast 171 | 'invoke \"~!', 172 | 'invoke \".*\\\\', 173 | 'invoke \"print32', 174 | # elem.wast 175 | 'module1', 176 | # conversions.wast 177 | 'reinterpret_f.*nan', 178 | # float_misc.wast 179 | 'f64.add.*0x1.fffffffffffffp\+969', 180 | # float_literals.wast 181 | 'invoke.*-hex-sep5', 182 | # float_exprs.wast 183 | 'nonarithmetic_nan_bitpattern.*03210' ) 184 | PY_SKIP_TESTS = ( 185 | # names.wast 186 | 'invoke \"~!', 187 | # conversions.wast 188 | '18446742974197923840.0', 189 | '18446744073709549568.0', 190 | '9223372036854775808', 191 | 'reinterpret_f.*nan', 192 | # endianness 193 | '.const 0x1.fff' ) 194 | 195 | def read_forms(string): 196 | forms = [] 197 | form = "" 198 | depth = 0 199 | line = 0 200 | pos = 0 201 | while pos < len(string): 202 | # Keep track of line number 203 | if string[pos] == '\n': line += 1 204 | 205 | # Ignore whitespace between top-level forms 206 | if string[pos] in (' ', '\n', '\t'): 207 | if depth != 0: 208 | form += string[pos] 209 | pos += 1 210 | continue 211 | 212 | #print("here0 line: %d, forms: %s" % (line, repr(forms))) 213 | # Add top-level comments 214 | if string[pos:pos+2] == ";;": 215 | print 216 | end = string.find("\n", pos) 217 | if end == -1: end == len(string) 218 | #form += string[pos:end] 219 | #forms.append(string[pos:end]) 220 | pos = end 221 | continue 222 | 223 | # TODO: handle nested multi-line comments 224 | if string[pos:pos+2] == "(;": 225 | # Skip multi-line comment 226 | end = string.find(";)", pos) 227 | if end == -1: 228 | raise Exception("mismatch multiline comment on line %d: '%s'" % ( 229 | line, string[pos:pos+80])) 230 | pos = end+2 231 | continue 232 | 233 | # handle strings 234 | if string[pos] == '"': 235 | end = string.find('"', pos+1) 236 | # TODO: fix when backslash itself may be quoted 237 | while string[end-1] == '\\': 238 | end = string.find('"', end+1) 239 | if end == -1: 240 | raise Exception("unterminated string line %d: '%s'" % ( 241 | line, string[pos:pos+80])) 242 | form += string[pos:end+1] 243 | pos = end+1 244 | continue 245 | 246 | # Read a top-level form 247 | if string[pos] == '(': depth += 1 248 | if string[pos] == ')': depth -= 1 249 | if depth == 0 and not form: 250 | raise Exception("garbage on line %d: '%s'" % ( 251 | line, string[pos:pos+80])) 252 | form += string[pos] 253 | if depth == 0 and form: 254 | forms.append(form) 255 | form = "" 256 | pos += 1 257 | return forms 258 | 259 | def parse_const(val): 260 | if val == '': 261 | return (None, '') 262 | type = val[0:3] 263 | if type in ["i32", "i64"]: 264 | if val[10:12] == "0x": 265 | return (int(val[10:], 16), 266 | "%s:%s" % (val[10:].lower(), type)) 267 | else: 268 | return (int(val[10:]), 269 | "%s:%s" % (hex(int(val[10:])), type)) 270 | elif type in ["f32", "f64"]: 271 | if val.find("nan:") >= 0: 272 | # TODO: how to handle this correctly 273 | return (float.fromhex(val[10:].split(':')[1]), 274 | "%s:%s" % (val[10:].split(':')[0], type)) 275 | elif val[10:12] == "0x" or val[10:13] == "-0x": 276 | return (float.fromhex(val[10:]), 277 | "%.7g:%s" % (float.fromhex(val[10:]), type)) 278 | else: 279 | return (float(val[10:]), 280 | "%.7g:%s" % (float(val[10:]), type)) 281 | else: 282 | raise Exception("invalid value '%s'" % val) 283 | 284 | def int2uint32(i): 285 | return i & 0xffffffff 286 | 287 | def int2int32(i): 288 | val = i & 0xffffffff 289 | if val & 0x80000000: 290 | return val - 0x100000000 291 | else: 292 | return val 293 | 294 | def int2uint64(i): 295 | return i & 0xffffffffffffffff 296 | 297 | def int2int64(i): 298 | val = i & 0xffffffffffffffff 299 | if val & 0x8000000000000000: 300 | return val - 0x10000000000000000 301 | else: 302 | return val 303 | 304 | 305 | def num_repr(i): 306 | if isinstance(i, int) or isinstance(i, long): 307 | return re.sub("L$", "", hex(i)) 308 | else: 309 | return "%.16g" % i 310 | 311 | def hexpad16(i): 312 | return "0x%04x" % i 313 | 314 | def hexpad24(i): 315 | return "0x%06x" % i 316 | 317 | def hexpad32(i): 318 | return "0x%08x" % i 319 | 320 | def hexpad64(i): 321 | return "0x%016x" % i 322 | 323 | def invoke(r, args, cmd): 324 | r.writeline(cmd.strip()) 325 | 326 | return r.read_to_prompt(['\r\nwebassembly> ', '\nwebassembly> '], 327 | timeout=args.test_timeout) 328 | 329 | def test_assert(r, opts, mode, cmd, expected): 330 | log("Testing(%s) %s = %s" % (mode, cmd, expected)) 331 | 332 | out = invoke(r, opts, cmd) 333 | outs = [''] + out.split('\n')[1:] 334 | out = outs[-1] 335 | 336 | if mode=='trap': 337 | o = re.sub('^Exception: ', '', out) 338 | e = re.sub('^Exception: ', '', expected) 339 | if o.find(e) >= 0 or e.find(o) >= 0: 340 | return True 341 | 342 | expects = set([expected]) 343 | m0 = re.search("^(-?[0-9\.e-]+):f32$", expected) 344 | if m0: 345 | if m0.group(1) == "-0": 346 | expects.add("0:f32") 347 | expects.add('%f:f32' % float(m0.group(1))) 348 | expects.add('%f:f32' % round(float(m0.group(1)),5)) 349 | if expected == "-nan:f32": 350 | expects.add("nan:f32") 351 | if expected == "nan:f32": 352 | expects.add("-nan:f32") 353 | if expected == "-nan:f64": 354 | expects.add("nan:f64") 355 | if expected == "nan:f64": 356 | expects.add("-nan:f64") 357 | 358 | # munge the output some 359 | out = re.sub("L:i32$", ':i32', out) 360 | out = re.sub("L:i64$", ':i64', out) 361 | results = set([out]) 362 | # create alternate representations 363 | m1 = re.search("^(-?[0-9a-fx]+):i32$", out) 364 | m2 = re.search("^(-?[0-9a-fx]+):i64$", out) 365 | m3 = re.search("^(-?[0-9\.e-]+):f32$", out) 366 | m4 = re.search("^(-?0x[0-9a-fp+\.]+):f32$", out) 367 | m5 = re.search("^(-?[0-9\.e-]+):f64$", out) 368 | m6 = re.search("^(-?0x[0-9a-fp+\.]+):f64$", out) 369 | if m1: 370 | val = int(m1.group(1), 16) 371 | results.add(num_repr(int2int32(val))+":i32") 372 | results.add(num_repr(int2uint32(val))+":i32") 373 | results.add(hexpad16(int2uint32(val))+":i32") 374 | results.add(hexpad24(int2uint32(val))+":i32") 375 | results.add(hexpad32(int2uint32(val))+":i32") 376 | elif m2: 377 | val = int(m2.group(1), 16) 378 | results.add(num_repr(int2int64(val))+":i64") 379 | results.add(num_repr(int2uint64(val))+":i64") 380 | results.add(hexpad32(int2uint64(val))+":i64") 381 | results.add(hexpad64(int2uint64(val))+":i64") 382 | elif m3: 383 | val = float(m3.group(1)) 384 | if re.search("^.*\.0+$", m3.group(1)): 385 | # Zero 386 | results.add('%d:f32' % int(val)) 387 | results.add('%.7g:f32' % val) 388 | else: 389 | results.add('%.7g:f32' % val) 390 | elif m4: 391 | val = float.fromhex(m4.group(1)) 392 | results.add("%f:f32" % val) 393 | results.add("%.7g:f32" % val) 394 | elif m5: 395 | val = float(m5.group(1)) 396 | if re.search("^.*\.0+$", m5.group(1)): 397 | # Zero 398 | results.add('%d:f64' % int(val)) 399 | results.add('%.7g:f64' % val) 400 | else: 401 | results.add('%.7g:f64' % val) 402 | elif m6: 403 | val = float.fromhex(m6.group(1)) 404 | results.add("%f:f64" % val) 405 | results.add("%.7g:f64" % val) 406 | 407 | if not expects.intersection(results): 408 | raise Exception("Failed:\n expected: '%s' %s\n got: '%s' %s" % ( 409 | expected, expects, out, results)) 410 | 411 | return True 412 | 413 | def test_assert_return(r, opts, form): 414 | # params, return 415 | m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\([^)]+\))\s*\)\s*$', form, re.S) 416 | if not m: 417 | # no params, return 418 | m = re.search('^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\([^)]+\))\s*\)\s*$', form, re.S) 419 | if not m: 420 | # params, no return 421 | m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S) 422 | if not m: 423 | # no params, no return 424 | m = re.search('^\(assert_return\s+\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S) 425 | if not m: 426 | raise Exception("unparsed assert_return: '%s'" % form) 427 | func = m.group(1) 428 | if m.group(2) == '': 429 | args = [] 430 | else: 431 | args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])] 432 | result, expected = parse_const(m.group(3)[1:-1]) 433 | 434 | test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected) 435 | 436 | def test_assert_trap(r, opts, form): 437 | # params 438 | m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s+(\(.*\))\s*\)\s*"([^"]+)"\s*\)\s*$', form) 439 | if not m: 440 | # no params 441 | m = re.search('^\(assert_trap\s+\(invoke\s+"([^"]*)"\s*()\)\s*"([^"]+)"\s*\)\s*$', form) 442 | if not m: 443 | raise Exception("unparsed assert_trap: '%s'" % form) 444 | func = m.group(1) 445 | if m.group(2) == '': 446 | args = [] 447 | else: 448 | args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])] 449 | expected = "Exception: %s" % m.group(3) 450 | 451 | test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected) 452 | 453 | def do_invoke(r, opts, form): 454 | # params 455 | m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form) 456 | if not m: 457 | # no params 458 | m = re.search('^\(invoke\s+"([^"]+)"\s*()\)\s*$', form) 459 | if not m: 460 | raise Exception("unparsed invoke: '%s'" % form) 461 | func = m.group(1) 462 | if m.group(2) == '': 463 | args = [] 464 | else: 465 | args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])] 466 | 467 | log("Invoking %s(%s)" % ( 468 | func, ", ".join([str(a) for a in args]))) 469 | 470 | invoke(r, opts, "%s %s" % (func, " ".join(args))) 471 | 472 | def skip_test(form, skip_list): 473 | for s in skip_list: 474 | if re.search(s, form): 475 | return True 476 | return False 477 | def is_ascii(s): 478 | return all(ord(c) > 8 and ord(c) < 128 for c in s) 479 | 480 | 481 | 482 | if __name__ == "__main__": 483 | opts = parser.parse_args(sys.argv[1:]) 484 | 485 | if opts.rundir: os.chdir(opts.rundir) 486 | 487 | if opts.log_file: log_file = open(opts.log_file, "a") 488 | if opts.debug_file: debug_file = open(opts.debug_file, "a") 489 | 490 | if opts.interpreter.endswith(".py"): 491 | SKIP_TESTS = PY_SKIP_TESTS 492 | else: 493 | SKIP_TESTS = C_SKIP_TESTS 494 | 495 | forms = read_forms(opts.test_file.read()) 496 | r = None 497 | 498 | for form in forms: 499 | try: 500 | (t1fd, wat_tempfile) = tempfile.mkstemp(suffix=".wat") 501 | (t2fd, wasm_tempfile) = tempfile.mkstemp(suffix=".wasm") 502 | os.close(t1fd) 503 | os.close(t2fd) 504 | 505 | if ";;" == form[0:2]: 506 | log(form) 507 | elif skip_test(form, SKIP_TESTS): 508 | log("Skipping test: %s" % form[0:60]) 509 | elif re.match("^\(assert_trap\s+\(module", form): 510 | log("ignoring assert_trap around module") 511 | elif re.match("^\(assert_exhaustion\\b.*", form): 512 | log("ignoring assert_exhaustion") 513 | elif re.match("^\(assert_unlinkable\\b.*", form): 514 | log("ignoring assert_unlinkable") 515 | elif re.match("^\(assert_malformed\\b.*", form): 516 | log("ignoring assert_malformed") 517 | elif re.match("^\(assert_return[_a-z]*_nan\\b.*", form): 518 | log("ignoring assert_return_.*_nan") 519 | elif re.match("^\(assert_return\s+\(get.*", form): 520 | log("ignoring assert_return (get") 521 | elif re.match(".*\(invoke\s+\$\\b.*", form): 522 | log("ignoring invoke $FOO") 523 | 524 | elif re.match("^\(module\\b.*", form): 525 | log("Writing WAT module to '%s'" % wat_tempfile) 526 | file(wat_tempfile, 'w').write(form) 527 | log("Compiling WASM to '%s'" % wasm_tempfile) 528 | cmd = [ opts.wat2wasm, 529 | "--no-check", 530 | wat_tempfile, 531 | "-o", 532 | wasm_tempfile ] 533 | log("Running: %s" % " ".join(cmd)) 534 | subprocess.check_call(cmd) 535 | 536 | log("Starting interpreter for module '%s'" % wasm_tempfile) 537 | cmd = [opts.interpreter, "--repl", wasm_tempfile] 538 | log("Running: %s" % " ".join(cmd)) 539 | r = Runner(cmd, no_pty=opts.no_pty) 540 | 541 | # Wait for the initial prompt 542 | try: 543 | assert_prompt(r, ['webassembly> '], opts.start_timeout) 544 | except: 545 | _, exc, _ = sys.exc_info() 546 | log("\nException: %s" % repr(exc)) 547 | log("Output before exception:\n%s" % r.buf) 548 | sys.exit(1) 549 | 550 | elif re.match("^\(assert_return\\b.*", form): 551 | if not is_ascii(form): 552 | log("Skipping assert_return with non-ASCII chars: %s" % form[0:60]) 553 | continue 554 | log("%s" % repr(form)) 555 | test_assert_return(r, opts, form) 556 | elif re.match("^\(assert_trap\\b.*", form): 557 | #log("%s" % form) 558 | test_assert_trap(r, opts, form) 559 | elif re.match("^\(invoke\\b.*", form): 560 | do_invoke(r, opts, form) 561 | elif re.match("^\(assert_invalid\\b.*", form): 562 | #log("ignoring assert_invalid") 563 | pass 564 | else: 565 | raise Exception("unrecognized form '%s...'" % form[0:40]) 566 | finally: 567 | if not opts.no_cleanup: 568 | if opts.verbose: 569 | log("Removing tempfiles: %s" % ( 570 | [wat_tempfile, wasm_tempfile])) 571 | os.remove(wat_tempfile) 572 | os.remove(wasm_tempfile) 573 | else: 574 | if opts.verbose: 575 | log("Leaving tempfiles: %s" % ( 576 | [wat_tempfile, wasm_tempfile])) 577 | -------------------------------------------------------------------------------- /thunk.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "util.h" 4 | #include "wa.h" 5 | #include "thunk.h" 6 | 7 | // 8 | // Outbound Thunks (calling imported functions) 9 | // 10 | 11 | void thunk_out(Module *m, uint32_t fidx) { 12 | Block *func = &m->functions[fidx]; 13 | Type *type = func->type; 14 | if (TRACE) { 15 | warn(" >>> thunk_out 0x%x(%d) %s.%s(", 16 | func->fidx, func->fidx, 17 | func->import_module, func->import_field); 18 | for (int p=type->param_count-1; p >= 0; p--) { 19 | warn("%s%s", value_repr(&m->stack[m->sp-p]), p ? " " : ""); 20 | } 21 | warn("), %d results\n", type->result_count); 22 | debug(" mask: 0x%x\n", type->mask); 23 | } 24 | 25 | switch (type->mask) { 26 | case 0x800 : THUNK_OUT_0(m, func, 0); break; 27 | case 0x8001 : THUNK_OUT_1(m, func, 0, i); break; 28 | case 0x80011 : THUNK_OUT_2(m, func, 0, i,i); break; 29 | case 0x800111 : THUNK_OUT_3(m, func, 0, i,i,i); break; 30 | case 0x8001111 : THUNK_OUT_4(m, func, 0, i,i,i,i); break; 31 | case 0x810 : THUNK_OUT_0(m, func, i); break; 32 | case 0x8101 : THUNK_OUT_1(m, func, i, i); break; 33 | case 0x81011 : THUNK_OUT_2(m, func, i, i,i); break; 34 | case 0x810111 : THUNK_OUT_3(m, func, i, i,i,i); break; 35 | case 0x8101111 : THUNK_OUT_4(m, func, i, i,i,i,i); break; 36 | case 0x81011111 : THUNK_OUT_5(m, func, i, i,i,i,i,i); break; 37 | case 0x8003 : THUNK_OUT_1(m, func, 0, f); break; 38 | case 0x80033 : THUNK_OUT_2(m, func, 0, f,f); break; 39 | case 0x800333 : THUNK_OUT_3(m, func, 0, f,f,f); break; 40 | case 0x8003333 : THUNK_OUT_4(m, func, 0, f,f,f,f); break; 41 | case 0x8303 : THUNK_OUT_1(m, func, f, f); break; 42 | case 0x8004 : THUNK_OUT_1(m, func, 0, F); break; 43 | case 0x80044 : THUNK_OUT_2(m, func, 0, F,F); break; 44 | case 0x800444 : THUNK_OUT_3(m, func, 0, F,F,F); break; 45 | case 0x8004444 : THUNK_OUT_4(m, func, 0, F,F,F,F); break; 46 | case 0x800444444 : THUNK_OUT_6(m, func, 0, F,F,F,F,F,F); break; 47 | case 0x8002 : THUNK_OUT_1(m, func, 0, I); break; 48 | case 0x8103 : THUNK_OUT_1(m, func, i, f); break; 49 | case 0x810121 : THUNK_OUT_3(m, func, i, i,I,i); break; 50 | case 0x8404 : THUNK_OUT_1(m, func, F, F); break; 51 | case 0x810111112211 : THUNK_OUT_9(m, func, i, i,i,i,i,i,I,I,i,i); break; 52 | // TODO: casting this can truncate the top of the mask 53 | default: FATAL("unsupported thunk_out mask 0x%x\n", (unsigned int)type->mask); 54 | } 55 | 56 | if (TRACE) { 57 | warn(" <<< thunk_out 0x%x(%d) %s.%s = %s\n", 58 | func->fidx, func->fidx, func->import_module, func->import_field, 59 | type->result_count > 0 ? value_repr(&m->stack[m->sp]) : "_"); 60 | } 61 | } 62 | 63 | 64 | // 65 | // Inbound Thunks (external calls into exported functions) 66 | // 67 | 68 | // TODO: global state, clean up somehow 69 | // This global is used by setup_thunk_in since signal handlers don't have 70 | // a way to pass arguments when they are setup. 71 | Module * _wa_current_module_; 72 | 73 | THUNK_IN_FN_0(m, 0) 74 | THUNK_IN_FN_2(m, 0, i,i) 75 | THUNK_IN_FN_1(m, 0, F) 76 | THUNK_IN_FN_1(m, i, i) 77 | THUNK_IN_FN_2(m, i, i,i) 78 | 79 | // Push arguments 80 | // return function pointer to thunk_in_* function 81 | void (*setup_thunk_in(uint32_t fidx))() { 82 | Module *m = _wa_current_module_; // TODO: global state, clean up somehow 83 | Block *func = &m->functions[fidx]; 84 | Type *type = func->type; 85 | 86 | // Make space on the stack 87 | m->sp += type->param_count; 88 | 89 | if (TRACE) { 90 | warn(" {{}} setup_thunk_in '%s', mask: 0x%x, ARGS FOR '>>' ARE BOGUS\n", 91 | func->export_name, type->mask); 92 | } 93 | 94 | // Do normal function call setup. The fp will point to the start of stack 95 | // elements that were just added above 96 | setup_call(m, fidx); 97 | 98 | // Set the type of the unset stack elements 99 | for(uint32_t p=0; pparam_count; p++) { 100 | m->stack[m->fp+p].value_type = type->params[p]; 101 | } 102 | 103 | // Return the thunk_in function 104 | void (*f)(void) = NULL; 105 | switch (type->mask) { 106 | case 0x800 : f = (void (*)(void)) thunk_in_0_0; break; 107 | case 0x8101 : f = (void (*)(void)) thunk_in_i_i; break; 108 | case 0x80011 : f = (void (*)(void)) thunk_in_0_ii; break; 109 | default: FATAL("unsupported thunk_in mask 0x%llx\n", type->mask); 110 | } 111 | 112 | return f; 113 | } 114 | 115 | void init_thunk_in(Module *m) { 116 | _wa_current_module_ = m; // TODO: global state, clean up somehow 117 | } 118 | 119 | -------------------------------------------------------------------------------- /thunk.h: -------------------------------------------------------------------------------- 1 | #ifndef _THUNK_H 2 | #define _THUNK_H 3 | 4 | #include "wa.h" 5 | 6 | #define TH_C_0 void 7 | #define TH_C_i uint32_t 8 | #define TH_C_I uint64_t 9 | #define TH_C_f float 10 | #define TH_C_F double 11 | #define TH_RES_0 12 | #define TH_RES_i uint32_t res = 13 | #define TH_RES_I uint64_t res = 14 | #define TH_RES_f float res = 15 | #define TH_RES_F double res = 16 | #define TH_RCNT_0 0 17 | #define TH_RCNT_i 1 18 | #define TH_RCNT_I 1 19 | #define TH_RCNT_f 1 20 | #define TH_RCNT_F 1 21 | #define TH_ATTR_i uint32 22 | #define TH_ATTR_I uint64 23 | #define TH_ATTR_f f32 24 | #define TH_ATTR_F f64 25 | #define TH_WA_i I32 26 | #define TH_WA_I I64 27 | #define TH_WA_f F32 28 | #define TH_WA_F F64 29 | #define TH_SP(M,OP,T) M->stack[M->sp OP].value.TH_ATTR_##T 30 | #define TH_FP(M,OP,T) M->stack[M->fp OP].value.TH_ATTR_##T 31 | #define TH_OUT_RET_(M,T) \ 32 | M->stack[M->sp].value_type = TH_WA_##T; \ 33 | TH_SP(M,,T) = res; 34 | #define TH_OUT_RET_0(M) 35 | #define TH_OUT_RET_i(M) TH_OUT_RET_(M,i) 36 | #define TH_OUT_RET_I(M) TH_OUT_RET_(M,I) 37 | #define TH_OUT_RET_f(M) TH_OUT_RET_(M,f) 38 | #define TH_OUT_RET_F(M) TH_OUT_RET_(M,F) 39 | #define TH_IN_RET_0(M) 40 | #define TH_IN_RET_i(M) TH_SP(M,--,i) 41 | #define TH_IN_RET_I(M) TH_SP(M,--,I) 42 | #define TH_IN_RET_f(M) TH_SP(M,--,f) 43 | #define TH_IN_RET_F(M) TH_SP(M,--,F) 44 | 45 | 46 | #define THUNK_OUT_0(M,FN,R) { \ 47 | TH_RES_##R ((TH_C_##R (*)())FN->func_ptr)(); \ 48 | M->sp += 0 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 49 | } 50 | #define THUNK_OUT_1(M,FN,R,A) { \ 51 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A))FN->func_ptr)(TH_SP(M,+0,A)); \ 52 | M->sp += -1 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 53 | } 54 | #define THUNK_OUT_2(M,FN,R,A,B) { \ 55 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A,TH_C_##B))FN->func_ptr)(TH_SP(M,-1,A),TH_SP(M,+0,B)); \ 56 | M->sp += -2 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 57 | } 58 | #define THUNK_OUT_3(M,FN,R,A,B,C) { \ 59 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A,TH_C_##B,TH_C_##C))FN->func_ptr)(TH_SP(M,-2,A),TH_SP(M,-1,B),TH_SP(M,+0,C)); \ 60 | M->sp += -3 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 61 | } 62 | #define THUNK_OUT_4(M,FN,R,A,B,C,D) { \ 63 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A,TH_C_##B,TH_C_##C,TH_C_##D))FN->func_ptr)(TH_SP(M,-3,A),TH_SP(M,-2,B),TH_SP(M,-1,C),TH_SP(M,+0,D)); \ 64 | M->sp += -4 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 65 | } 66 | #define THUNK_OUT_5(M,FN,R,A,B,C,D,E) { \ 67 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A,TH_C_##B,TH_C_##C,TH_C_##D,TH_C_##E))FN->func_ptr)(TH_SP(M,-4,A),TH_SP(M,-3,B),TH_SP(M,-2,C),TH_SP(M,-1,D),TH_SP(M,+0,E)); \ 68 | M->sp += -5 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 69 | } 70 | #define THUNK_OUT_6(M,FN,R,A,B,C,D,E,F) { \ 71 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A,TH_C_##B,TH_C_##C,TH_C_##D,TH_C_##E,TH_C_##F))FN->func_ptr)(TH_SP(M,-5,A),TH_SP(M,-4,B),TH_SP(M,-3,C),TH_SP(M,-2,D),TH_SP(M,-1,E),TH_SP(M,+0,F)); \ 72 | M->sp += -6 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 73 | } 74 | #define THUNK_OUT_9(M,FN,R,A,B,C,D,E,F,G,H,I) { \ 75 | TH_RES_##R ((TH_C_##R (*)(TH_C_##A,TH_C_##B,TH_C_##C,TH_C_##D,TH_C_##E,TH_C_##F,TH_C_##G,TH_C_##H,TH_C_##I))FN->func_ptr)(TH_SP(M,-8,A),TH_SP(M,-7,B),TH_SP(M,-6,C),TH_SP(M,-5,D),TH_SP(M,-4,E),TH_SP(M,-3,F),TH_SP(M,-2,G),TH_SP(M,-1,H),TH_SP(M,+0,I)); \ 76 | M->sp += -9 + TH_RCNT_##R; TH_OUT_RET_##R(M); \ 77 | } 78 | 79 | #define THUNK_IN_FN_0(M,R) \ 80 | TH_C_##R thunk_in_##R##_0() { \ 81 | Module *m = _wa_current_module_; \ 82 | interpret(M); return TH_IN_RET_##R(M); \ 83 | } 84 | #define THUNK_IN_FN_1(M,R,A) \ 85 | TH_C_##R thunk_in_##R##_##A(TH_C_##A a) { \ 86 | Module *m = _wa_current_module_; \ 87 | TH_FP(M,+0,A) = a; \ 88 | interpret(M); return TH_IN_RET_##R(M); \ 89 | } 90 | #define THUNK_IN_FN_2(M,R,A,B) \ 91 | TH_C_##R thunk_in_##R##_##A##B(TH_C_##A a, TH_C_##B b) { \ 92 | Module *m = _wa_current_module_; \ 93 | TH_FP(M,+0,A) = a; TH_FP(M,+1,B) = b; \ 94 | interpret(M); return TH_IN_RET_##R(M); \ 95 | } 96 | #define THUNK_IN_FN_3(M,R,A,B,C) \ 97 | TH_C_##R thunk_in_##R##_##A##B##C(TH_C_##A a, TH_C_##B b, TH_C_##C c) { \ 98 | Module *m = _wa_current_module_; \ 99 | TH_FP(M,+0,A) = a; TH_FP(M,+1,B) = b; TH_FP(M,+2,C) = c; \ 100 | interpret(M); return TH_IN_RET_##R(M); \ 101 | } 102 | 103 | 104 | void thunk_out(Module *m, uint32_t fidx); 105 | 106 | void init_thunk_in(Module *m); 107 | 108 | #endif // _THUNK_H 109 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "util.h" 11 | 12 | // type readers 13 | 14 | uint64_t read_LEB_(uint8_t *bytes, uint32_t *pos, uint32_t maxbits, bool sign) { 15 | uint64_t result = 0; 16 | uint32_t shift = 0; 17 | uint32_t bcnt = 0; 18 | uint32_t startpos = *pos; 19 | uint64_t byte; 20 | 21 | while (true) { 22 | byte = bytes[*pos]; 23 | *pos += 1; 24 | result |= ((byte & 0x7f)< (maxbits + 7 - 1) / 7) { 31 | FATAL("Unsigned LEB at byte %d overflow", startpos); 32 | } 33 | } 34 | if (sign && (shift < maxbits) && (byte & 0x40)) { 35 | // Sign extend 36 | result |= - (1 << shift); 37 | } 38 | return result; 39 | } 40 | 41 | uint64_t read_LEB(uint8_t *bytes, uint32_t *pos, uint32_t maxbits) { 42 | return read_LEB_(bytes, pos, maxbits, false); 43 | } 44 | 45 | uint64_t read_LEB_signed(uint8_t *bytes, uint32_t *pos, uint32_t maxbits) { 46 | return read_LEB_(bytes, pos, maxbits, true); 47 | } 48 | 49 | uint32_t read_uint32(uint8_t *bytes, uint32_t *pos) { 50 | *pos += 4; 51 | return ((uint32_t *) (bytes+*pos-4))[0]; 52 | } 53 | 54 | // Reads a string from the bytes array at pos that starts with a LEB length 55 | // if result_len is not NULL, then it will be set to the string length 56 | char *read_string(uint8_t *bytes, uint32_t *pos, uint32_t *result_len) { 57 | uint32_t str_len = read_LEB(bytes, pos, 32); 58 | char * str = malloc(str_len+1); 59 | memcpy(str, bytes+*pos, str_len); 60 | str[str_len] = '\0'; 61 | *pos += str_len; 62 | if (result_len) { *result_len = str_len; } 63 | return str; 64 | } 65 | 66 | // Math 67 | 68 | // Inplace sign extend 69 | void sext_8_32(uint32_t *val) { 70 | if (*val & 0x80) { *val = *val | 0xffffff00; } 71 | } 72 | void sext_16_32(uint32_t *val) { 73 | if (*val & 0x8000) { *val = *val | 0xffff0000; } 74 | } 75 | void sext_8_64(uint64_t *val) { 76 | if (*val & 0x80) { *val = *val | 0xffffffffffffff00; } 77 | } 78 | void sext_16_64(uint64_t *val) { 79 | if (*val & 0x8000) { *val = *val | 0xffffffffffff0000; } 80 | } 81 | void sext_32_64(uint64_t *val) { 82 | if (*val & 0x80000000) { *val = *val | 0xffffffff00000000; } 83 | } 84 | 85 | // Based on: http://stackoverflow.com/a/776523/471795 86 | uint32_t rotl32(uint32_t n, unsigned int c) { 87 | const unsigned int mask = (CHAR_BIT*sizeof(n)-1); 88 | c = c % 32; 89 | c &= mask; 90 | return (n<>( (-c)&mask )); 91 | } 92 | 93 | uint32_t rotr32(uint32_t n, unsigned int c) { 94 | const unsigned int mask = (CHAR_BIT*sizeof(n)-1); 95 | c = c % 32; 96 | c &= mask; 97 | return (n>>c) | (n<<( (-c)&mask )); 98 | } 99 | 100 | uint64_t rotl64(uint64_t n, unsigned int c) { 101 | const unsigned int mask = (CHAR_BIT*sizeof(n)-1); 102 | c = c % 64; 103 | c &= mask; 104 | return (n<>( (-c)&mask )); 105 | } 106 | 107 | uint64_t rotr64(uint64_t n, unsigned int c) { 108 | const unsigned int mask = (CHAR_BIT*sizeof(n)-1); 109 | c = c % 64; 110 | c &= mask; 111 | return (n>>c) | (n<<( (-c)&mask )); 112 | } 113 | 114 | double wa_fmax(double a, double b) { 115 | double c = fmax(a, b); 116 | if (c==0 && a==b) { return signbit(a) ? b : a; } 117 | return c; 118 | } 119 | double wa_fmin(double a, double b) { 120 | double c = fmin(a, b); 121 | if (c==0 && a==b) { return signbit(a) ? a : b; } 122 | return c; 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define DEBUG 0 9 | #define INFO 0 10 | #define WARN 0 11 | #define TRACE 0 12 | 13 | //#define DEBUG 1 14 | //#define INFO 1 15 | //#define WARN 1 16 | //#define TRACE 1 17 | 18 | #define FATAL(...) { \ 19 | fprintf(stderr, "Error(%s:%d): ", __FILE__, __LINE__); \ 20 | fprintf(stderr, __VA_ARGS__); exit(1); \ 21 | } 22 | 23 | #define ASSERT(exp, ...) { \ 24 | if (! (exp)) { \ 25 | fprintf(stderr, "Assert Failed (%s:%d): ", __FILE__, __LINE__); \ 26 | fprintf(stderr, __VA_ARGS__); exit(1); \ 27 | } \ 28 | } 29 | 30 | #if TRACE 31 | #define trace(...) fprintf(stderr, __VA_ARGS__); 32 | #else 33 | #define trace(...) ; 34 | #endif 35 | 36 | #if DEBUG 37 | #define debug(...) fprintf(stderr, __VA_ARGS__); 38 | #else 39 | #define debug(...) ; 40 | #endif 41 | 42 | #if INFO 43 | #define info(...) fprintf(stderr, __VA_ARGS__); 44 | #else 45 | #define info(...) ; 46 | #endif 47 | 48 | #if WARN 49 | #define warn(...) fprintf(stderr, __VA_ARGS__); 50 | #else 51 | #define warn(...) ; 52 | #endif 53 | 54 | #define log(...) fprintf(stderr, __VA_ARGS__); 55 | 56 | #define error(...) fprintf(stderr, __VA_ARGS__); 57 | 58 | 59 | uint64_t read_LEB(uint8_t *bytes, uint32_t *pos, uint32_t maxbits); 60 | uint64_t read_LEB_signed(uint8_t *bytes, uint32_t *pos, uint32_t maxbits); 61 | 62 | uint32_t read_uint32(uint8_t *bytes, uint32_t *pos); 63 | 64 | char *read_string(uint8_t *bytes, uint32_t *pos, uint32_t *result_len); 65 | 66 | // Math 67 | void sext_8_32(uint32_t *val); 68 | void sext_16_32(uint32_t *val); 69 | void sext_8_64(uint64_t *val); 70 | void sext_16_64(uint64_t *val); 71 | void sext_32_64(uint64_t *val); 72 | uint32_t rotl32 (uint32_t n, unsigned int c); 73 | uint32_t rotr32 (uint32_t n, unsigned int c); 74 | uint64_t rotl64(uint64_t n, unsigned int c); 75 | uint64_t rotr64(uint64_t n, unsigned int c); 76 | double wa_fmax(double a, double b); 77 | double wa_fmin(double a, double b); 78 | 79 | #endif // of UTIL_H 80 | -------------------------------------------------------------------------------- /wa.h: -------------------------------------------------------------------------------- 1 | #ifndef WAC_H 2 | #define WAC_H 3 | 4 | #include 5 | #include 6 | 7 | #define WA_MAGIC 0x6d736100 8 | #define WA_VERSION 0x01 9 | 10 | #define PAGE_SIZE 0x10000 // 65536 11 | #define STACK_SIZE 0x10000 // 65536 12 | #define BLOCKSTACK_SIZE 0x1000 // 4096 13 | #define CALLSTACK_SIZE 0x1000 // 4096 14 | #define BR_TABLE_SIZE 0x10000 // 65536 15 | 16 | #define I32 0x7f // -0x01 17 | #define I64 0x7e // -0x02 18 | #define F32 0x7d // -0x03 19 | #define F64 0x7c // -0x04 20 | #define ANYFUNC 0x70 // -0x10 21 | #define FUNC 0x60 // -0x20 22 | #define BLOCK 0x40 // -0x40 23 | 24 | #define KIND_FUNCTION 0 25 | #define KIND_TABLE 1 26 | #define KIND_MEMORY 2 27 | #define KIND_GLOBAL 3 28 | 29 | typedef struct Type { 30 | uint8_t form; 31 | uint32_t param_count; 32 | uint32_t *params; 33 | uint32_t result_count; 34 | uint32_t *results; 35 | uint64_t mask; // unique mask value for each type 36 | } Type; 37 | 38 | typedef union FuncPtr { 39 | void (*void_void) (); 40 | void (*void_i32) (uint32_t); 41 | void (*void_i64) (uint64_t); 42 | void (*void_f32) (float); 43 | void (*void_f64) (double); 44 | 45 | double (*f64_f64) (double); 46 | } FuncPtr; 47 | 48 | // A block or function 49 | typedef struct Block { 50 | uint8_t block_type; // 0x00: function, 0x01: init_exp 51 | // 0x02: block, 0x03: loop, 0x04: if 52 | uint32_t fidx; // function only (index) 53 | Type *type; // params/results type 54 | uint32_t local_count; // function only 55 | uint32_t *locals; // function only 56 | uint32_t start_addr; 57 | uint32_t end_addr; 58 | uint32_t else_addr; // if block only 59 | uint32_t br_addr; // blocks only 60 | char *export_name; // function only (exported) 61 | char *import_module; // function only (imported) 62 | char *import_field; // function only (imported) 63 | void *(*func_ptr)(); // function only (imported) 64 | } Block; 65 | 66 | /// 67 | 68 | typedef struct StackValue { 69 | uint8_t value_type; 70 | union { 71 | uint32_t uint32; 72 | int32_t int32; 73 | uint64_t uint64; 74 | int64_t int64; 75 | float f32; 76 | double f64; 77 | } value; 78 | } StackValue; 79 | 80 | typedef struct Frame { 81 | Block *block; 82 | // Saved state 83 | int sp; 84 | int fp; 85 | uint32_t ra; 86 | } Frame; 87 | 88 | /// 89 | 90 | typedef struct Table { 91 | uint8_t elem_type; // type of entries (only ANYFUNC in MVP) 92 | uint32_t initial; // initial table size 93 | uint32_t maximum; // maximum table size 94 | uint32_t size; // current table size 95 | uint32_t *entries; 96 | } Table; 97 | 98 | typedef struct Memory { 99 | uint32_t initial; // initial size (64K pages) 100 | uint32_t maximum; // maximum size (64K pages) 101 | uint32_t pages; // current size (64K pages) 102 | uint8_t *bytes; // memory area 103 | char *export_name; // when exported 104 | } Memory; 105 | 106 | typedef struct Export { 107 | uint32_t external_kind; 108 | char *export_name; 109 | void *value; 110 | } Export; 111 | 112 | typedef struct Options { 113 | // when true: host memory addresses will be outside allocated memory area 114 | // so do not do bounds checking 115 | bool disable_memory_bounds; 116 | 117 | // when true, table entries are accessed like this: 118 | // m->table.entries[m->table.entries-index] 119 | // when false, table entires are accessed like this: 120 | // m->table.entries[index] 121 | bool mangle_table_index; 122 | 123 | bool dlsym_trim_underscore; 124 | } Options; 125 | 126 | 127 | typedef struct Module { 128 | char *path; // file path of the wasm module 129 | Options options; // Config options 130 | 131 | uint32_t byte_count; // number of bytes in the module 132 | uint8_t *bytes; // module content/bytes 133 | 134 | uint32_t type_count; // number of function types 135 | Type *types; // function types 136 | 137 | uint32_t import_count; // number of leading imports in functions 138 | uint32_t function_count; // number of function (including imports) 139 | Block *functions; // imported and locally defined functions 140 | Block **block_lookup; // map of module byte position to Blocks 141 | // same length as byte_count 142 | uint32_t start_function; // function to run on module load 143 | 144 | Table table; 145 | 146 | Memory memory; 147 | 148 | uint32_t global_count; // number of globals 149 | StackValue *globals; // globals 150 | 151 | uint32_t export_count; // number of exports 152 | Export *exports; 153 | 154 | // Runtime state 155 | uint32_t pc; // program counter 156 | int sp; // operand stack pointer 157 | int fp; // current frame pointer into stack 158 | StackValue stack[STACK_SIZE]; // main operand stack 159 | int csp; // callstack pointer 160 | Frame callstack[CALLSTACK_SIZE]; // callstack 161 | uint32_t br_table[BR_TABLE_SIZE]; // br_table branch indexes 162 | } Module; 163 | 164 | // 165 | // Function declarations (Public API) 166 | // 167 | 168 | extern char exception[]; 169 | uint64_t get_type_mask(Type *type); 170 | char *value_repr(StackValue *v); 171 | void (*setup_thunk_in(uint32_t fidx))(); 172 | void setup_call(Module *m, uint32_t fidx); 173 | bool interpret(Module *m); 174 | 175 | void *get_export(Module *m, char *name, uint32_t kind); 176 | Module *load_module(uint8_t *bytes, uint32_t byte_count, Options options); 177 | bool invoke(Module *m, uint32_t fidx); 178 | 179 | #endif // of WAC_H 180 | -------------------------------------------------------------------------------- /wac.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #if PLATFORM==2 10 | #include 11 | #endif 12 | 13 | #include "util.h" 14 | #include "platform.h" 15 | #include "wa.h" 16 | 17 | void usage(char *prog) { 18 | fprintf(stderr, "%s --repl WASM_FILE\n", prog); 19 | fprintf(stderr, "%s WASM_FILE CMD [ARG...]\n", prog); 20 | exit(2); 21 | } 22 | 23 | // 24 | // Imports used by specification tests 25 | // 26 | 27 | // https://github.com/WebAssembly/spec/blob/master/interpreter/host/spectest.ml 28 | #define PAGE_COUNT 1 29 | #define TABLE_COUNT 20 30 | Memory _spectest__memory_ = {PAGE_COUNT, PAGE_COUNT, PAGE_COUNT, NULL}; 31 | Table _spectest__table_ = {ANYFUNC, TABLE_COUNT, TABLE_COUNT, TABLE_COUNT, NULL}; 32 | uint32_t _spectest__global_i32_ = 666; 33 | 34 | void _spectest__print_(uint32_t val) { 35 | //printf("spectest.print 0x%x:i32\n", val); 36 | printf("0x%x:i32\n", val); 37 | } 38 | void _spectest__print_i32_(uint32_t val) { 39 | printf("0x%x:i32\n", val); 40 | } 41 | 42 | // 43 | // Command line parsing functions 44 | // 45 | 46 | // Split a space separated string into an argv style array of strings 47 | // Destroys str, sets argc to number of args, return static buffer argv_buf 48 | // Returns 0 on failure 49 | char *argv_buf[100]; 50 | char **split_argv(char *str, int *argc) { 51 | argv_buf[(*argc)++] = str; 52 | 53 | for (int i = 1; str[i] != '\0'; i += 1) { 54 | if (str[i-1] == ' ') { 55 | str[i-1] = '\0'; 56 | argv_buf[(*argc)++] = str + i; 57 | } 58 | } 59 | argv_buf[(*argc)] = NULL; 60 | return argv_buf; 61 | } 62 | 63 | // Parse and add arguments to the stack 64 | void parse_args(Module *m, Type *type, int argc, char **argv) { 65 | for (int i=0; isp++; 70 | StackValue *sv = &m->stack[m->sp]; 71 | sv->value_type = type->params[i]; 72 | switch (type->params[i]) { 73 | case I32: sv->value.uint32 = strtoul(argv[i], NULL, 0); break; 74 | case I64: sv->value.uint64 = strtoull(argv[i], NULL, 0); break; 75 | case F32: if (strncmp("-nan", argv[i], 4) == 0) { 76 | sv->value.f32 = -NAN; 77 | } else { 78 | sv->value.f32 = atof(argv[i]); 79 | }; break; 80 | case F64: if (strncmp("-nan", argv[i], 4) == 0) { 81 | sv->value.f64 = -NAN; 82 | } else { 83 | sv->value.f64 = atof(argv[i]); 84 | }; break; 85 | } 86 | } 87 | } 88 | 89 | 90 | int main(int argc, char **argv) { 91 | char *mod_path; 92 | int res = 0; 93 | uint8_t *bytes = NULL; 94 | int byte_count; 95 | int optidx, repl = 0; 96 | char *line = NULL; 97 | 98 | // Parse arguments 99 | if (argc < 3) { usage(argv[0]); } 100 | if (strcmp("--repl", argv[1]) == 0) { 101 | if (argc > 3) { usage(argv[0]); } 102 | repl = 1; 103 | optidx = 2; 104 | } else { 105 | optidx = 1; 106 | } 107 | 108 | mod_path = argv[optidx++]; 109 | 110 | // Allocate memory and table used for spec test 111 | _spectest__memory_.bytes = malloc(PAGE_COUNT * PAGE_SIZE); 112 | _spectest__table_.entries = malloc(TABLE_COUNT); 113 | 114 | // Load the module 115 | #if PLATFORM==1 116 | // open and mmap the WASM module 117 | bytes = mmap_file(mod_path, &byte_count); 118 | #else 119 | line = malloc(256); 120 | 121 | // read the file via mem_fs or serial method 122 | byte_count = vfs_file_size(mod_path); 123 | if (byte_count > 0) { 124 | bytes = malloc(byte_count); 125 | vfs_read_file(mod_path, (char *)bytes); 126 | } 127 | #endif 128 | if (bytes == NULL) { 129 | fprintf(stderr, "Could not load %s", mod_path); 130 | return 2; 131 | } 132 | 133 | Options opts; 134 | Module *m = load_module(bytes, byte_count, opts); 135 | m->path = mod_path; 136 | 137 | for (;;) { 138 | if (repl) { 139 | #if PLATFORM==1 140 | line = readline("webassembly> "); 141 | if (!line) { break; } 142 | #else 143 | if (!readline("webassembly> ", line, 1024)) { 144 | break; 145 | } 146 | #endif 147 | argc = 0; 148 | optidx = 0; 149 | argv = split_argv(line, &argc); 150 | if (argc == 0) { continue; } 151 | } 152 | 153 | // Reset the stacks 154 | m->sp = -1; 155 | m->fp = -1; 156 | m->csp = -1; 157 | 158 | Block *func = get_export(m, argv[optidx], KIND_FUNCTION); 159 | if (!func) { 160 | error("no exported function named '%s'\n", argv[optidx]); 161 | if (repl) { continue; } 162 | return 1; 163 | } 164 | parse_args(m, func->type, argc-optidx-1, argv+optidx+1); 165 | warn("Running '%s' function 0x%x ('%s')\n", m->path, func->fidx, entry); 166 | res = invoke(m, func->fidx); 167 | if (res) { 168 | if (m->sp >= 0) { 169 | printf("%s\n", value_repr(&m->stack[m->sp])); 170 | } 171 | } else { 172 | error("Exception: %s\n", exception); 173 | if (!repl) { return 1; } 174 | } 175 | 176 | if (repl) { 177 | #if PLATFORM==1 178 | free(line); 179 | #endif 180 | continue; 181 | } 182 | break; 183 | } 184 | 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /wace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #if PLATFORM==2 6 | #include 7 | #endif 8 | 9 | #include "util.h" 10 | #include "platform.h" 11 | #include "wa.h" 12 | #include "thunk.h" 13 | 14 | #if PLATFORM==1 15 | #include "wace_emscripten.h" 16 | #define MANGLE_TABLE_INDEX true 17 | #elif PLATFORM==2 18 | #include "wace_fooboot.h" 19 | #define MANGLE_TABLE_INDEX false 20 | #else 21 | #error "unknown PLATFORM" 22 | #endif 23 | 24 | void usage(char *prog) { 25 | fprintf(stderr, "%s WASM_FILE [ARG...]\n", prog); 26 | exit(2); 27 | } 28 | 29 | ///////////////////////////////////////////////////////// 30 | // Command line 31 | 32 | int main(int argc, char **argv) { 33 | char *mod_path; 34 | int res = 0; 35 | uint8_t *bytes = NULL; 36 | int byte_count; 37 | 38 | // Parse arguments 39 | if (argc < 2) { usage(argv[0]); } 40 | mod_path = argv[1]; 41 | 42 | init_wace(); 43 | 44 | // Load the module 45 | #if PLATFORM==1 46 | // open and mmap the WASM module 47 | bytes = mmap_file(mod_path, &byte_count); 48 | #else 49 | // read the file via mem_fs or serial method 50 | byte_count = vfs_file_size(mod_path); 51 | if (byte_count > 0) { 52 | bytes = malloc(byte_count); 53 | vfs_read_file(mod_path, (char *)bytes); 54 | } 55 | #endif 56 | if (bytes == NULL) { 57 | fprintf(stderr, "Could not load %s", mod_path); 58 | return 2; 59 | } 60 | 61 | Options opts = { .disable_memory_bounds = true, 62 | .mangle_table_index = MANGLE_TABLE_INDEX, 63 | .dlsym_trim_underscore = true }; 64 | Module *m = load_module(bytes, byte_count, opts); 65 | m->path = mod_path; 66 | 67 | init_thunk_in(m); 68 | 69 | // emscripten initialization 70 | Block *func = get_export(m, "__post_instantiate", KIND_FUNCTION); 71 | if (func) { 72 | res = invoke(m, func->fidx); 73 | } 74 | 75 | // setup argc/argv 76 | m->stack[++m->sp].value_type = I32; 77 | m->stack[m->sp].value.uint32 = argc-1; 78 | m->stack[++m->sp].value_type = I32; 79 | m->stack[m->sp].value.uint32 = (uint32_t)(argv+1); 80 | 81 | // Invoke main/_main function and exit 82 | func = get_export(m, "main", KIND_FUNCTION); 83 | if (!func) { 84 | func = get_export(m, "_main", KIND_FUNCTION); 85 | if (!func) { 86 | FATAL("no exported function named 'main' or '_main'\n"); 87 | } 88 | } 89 | res = invoke(m, func->fidx); 90 | 91 | if (!res) { 92 | error("Exception: %s\n", exception); 93 | return 1; 94 | } 95 | 96 | if (m->sp >= 0) { 97 | return (m->stack[m->sp--].value.uint32); 98 | } else { 99 | return 0; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /wace_emscripten.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // Call table/trapping table lookups/execution 10 | #include 11 | #include 12 | #include 13 | 14 | #include "util.h" 15 | #include "wa.h" 16 | 17 | ///////////////////////////////////////////////////////// 18 | // emscripten memory layout 19 | 20 | #define PAGE_COUNT 256 // 64K * 256 = 16MB 21 | #define TOTAL_PAGES ULONG_MAX / PAGE_SIZE 22 | #define TABLE_COUNT 256 23 | 24 | Memory _env__memory_ = {TOTAL_PAGES, TOTAL_PAGES, TOTAL_PAGES, NULL}; 25 | uint8_t *_env__memoryBase_; 26 | 27 | Table _env__table_ = {ANYFUNC, TABLE_COUNT, TABLE_COUNT, TABLE_COUNT, 0}; 28 | //uint32_t *_env__table_ = 0; 29 | uint32_t *_env__tableBase_; 30 | 31 | double _global__NaN_ = NAN; 32 | double _global__Infinity_ = INFINITY; 33 | 34 | uint32_t **_env__DYNAMICTOP_PTR_; 35 | uint32_t *_env__tempDoublePtr_; 36 | 37 | 38 | // Initialize function/jump table 39 | void segv_thunk_handler(int cause, siginfo_t * info, void *uap) { 40 | int index = (info->si_addr - (void *)_env__table_.entries); 41 | if (info->si_addr >= (void *)_env__table_.entries && 42 | (info->si_addr - (void *)_env__table_.entries) < TABLE_COUNT) { 43 | uint32_t fidx = _env__table_.entries[index]; 44 | ucontext_t *context = uap; 45 | void (*f)(void); 46 | f = setup_thunk_in(fidx); 47 | // Test/debug only (otherwise I/O should be avoided in a signal handlers) 48 | //printf("SIGSEGV raised at address %p, index: %d, fidx: %d\n", 49 | // info->si_addr, index, fidx); 50 | 51 | // On Linux x86, general register 14 is EIP 52 | context->uc_mcontext.gregs[14] = (unsigned int)f; 53 | } else { 54 | // What to do here? 55 | } 56 | } 57 | 58 | void init_thunk_in_trap() { 59 | // Trap on sigsegv 60 | struct sigaction sa; 61 | sa.sa_sigaction = segv_thunk_handler; 62 | sa.sa_flags = SA_SIGINFO; 63 | sigemptyset (&sa.sa_mask); 64 | if (sigaction (SIGSEGV, &sa, 0)) { 65 | perror ("sigaction"); 66 | exit(1); 67 | } 68 | 69 | // Allow READ/WRITE but prevent execute. This only works if PROT_EXEC does 70 | // in fact trap 71 | debug("mprotect on table at: %p\n", _env__table_.entries); 72 | if (mprotect(_env__table_.entries, TABLE_COUNT*sizeof(uint32_t), 73 | PROT_READ | PROT_WRITE)) { 74 | perror("mprotect"); 75 | exit(1); 76 | } 77 | } 78 | 79 | // Initialize memory globals and function/jump table 80 | void init_wace() { 81 | _env__memoryBase_ = calloc(PAGE_COUNT, PAGE_SIZE); 82 | 83 | _env__tempDoublePtr_ = (uint32_t*)_env__memoryBase_; 84 | _env__DYNAMICTOP_PTR_ = (uint32_t**)(_env__memoryBase_ + 16); 85 | 86 | *_env__DYNAMICTOP_PTR_ = (uint32_t*)(_env__memoryBase_ + PAGE_COUNT * PAGE_SIZE); 87 | 88 | // This arrangement correlates to the module mangle_table_offset option 89 | if (posix_memalign((void **)&_env__table_.entries, sysconf(_SC_PAGESIZE), 90 | TABLE_COUNT*sizeof(uint32_t))) { 91 | perror("posix_memalign"); 92 | exit(1); 93 | } 94 | _env__tableBase_ = _env__table_.entries; 95 | 96 | info("init_mem results:\n"); 97 | info(" _env__memory_.bytes: %p\n", _env__memory_.bytes); 98 | info(" _env__memoryBase_: %p\n", _env__memoryBase_); 99 | info(" _env__DYNAMIC_TOP_PTR_: %p\n", _env__DYNAMICTOP_PTR_); 100 | info(" *_env__DYNAMIC_TOP_PTR_: %p\n", *_env__DYNAMICTOP_PTR_); 101 | info(" _env__table_.entries: %p\n", _env__table_.entries); 102 | info(" _env__tableBase_: 0x%x\n", _env__tableBase_); 103 | 104 | init_thunk_in_trap(); 105 | } 106 | 107 | 108 | ///////////////////////////////////////////////////////// 109 | // General globals/imports 110 | 111 | uint32_t _env__ABORT_ = 0; 112 | 113 | #include 114 | int _env___printf_(const char * fmt, va_list args) { 115 | return vprintf(fmt, args); 116 | } 117 | 118 | void _env__abortStackOverflow_(uint32_t x) { 119 | FATAL("_env__abortStackOverflow 0x%x\n", x); 120 | } 121 | 122 | void _env__nullFunc_X_(uint32_t x) { 123 | FATAL("_env__nullFunc_X_ 0x%x\n", x); 124 | } 125 | -------------------------------------------------------------------------------- /wace_emscripten.h: -------------------------------------------------------------------------------- 1 | #ifndef WACE_EMSCRIPTEN_H 2 | #define WACE_EMSCRIPTEN_H 3 | 4 | void init_wace(); 5 | 6 | #endif // WACE_EMSCRIPTEN_H 7 | -------------------------------------------------------------------------------- /wace_fooboot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "wa.h" 7 | 8 | ///////////////////////////////////////////////////////// 9 | // memory layout 10 | 11 | #define PAGE_COUNT 256 // 64K * 256 = 16MB 12 | #define TOTAL_PAGES ULONG_MAX / PAGE_SIZE 13 | #define TABLE_COUNT 256 14 | 15 | Memory memory = {TOTAL_PAGES, TOTAL_PAGES, TOTAL_PAGES, 0}; 16 | uint8_t *memoryBase; 17 | 18 | // Initialize memory globals 19 | void init_wace() { 20 | memoryBase = calloc(PAGE_COUNT, PAGE_SIZE); 21 | //printf("memory: %p\n", memory); 22 | //printf("memoryBase: %p\n", memoryBase); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /wace_fooboot.h: -------------------------------------------------------------------------------- 1 | #ifndef WACE_OS_H 2 | #define WACE_OS_H 3 | 4 | void init_wace(); 5 | 6 | #endif // WACE_OS_H 7 | -------------------------------------------------------------------------------- /wasi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "util.h" 15 | #include "wa.h" 16 | #include "wasi.h" 17 | 18 | // 19 | // WASI implemenation (https://wasi.dev) 20 | // 21 | 22 | // Memory and utilities 23 | Memory *wasi_memory = NULL; 24 | 25 | int host_argc = 0; 26 | char **host_argv = NULL; 27 | char **host_envp = NULL; 28 | 29 | #define MAX_IOV 128 30 | struct iovec host_iov[MAX_IOV]; 31 | 32 | #define PREOPEN_CNT 7 33 | Preopen preopen[PREOPEN_CNT] = { 34 | { .path = "", .path_len = 7, }, 35 | { .path = "", .path_len = 8, }, 36 | { .path = "", .path_len = 8, }, 37 | { .path = "./", .path_len = 2, }, 38 | { .path = "../", .path_len = 3, }, 39 | { .path = "/", .path_len = 1, }, 40 | { .path = "/tmp", .path_len = 4, }, 41 | }; 42 | 43 | __wasi_errno_t errno_to_wasi(int errnum) { 44 | switch (errnum) { 45 | case EPERM: return __WASI_EPERM; break; 46 | case ENOENT: return __WASI_ENOENT; break; 47 | case ESRCH: return __WASI_ESRCH; break; 48 | case EINTR: return __WASI_EINTR; break; 49 | case EIO: return __WASI_EIO; break; 50 | case ENXIO: return __WASI_ENXIO; break; 51 | case E2BIG: return __WASI_E2BIG; break; 52 | case ENOEXEC: return __WASI_ENOEXEC; break; 53 | case EBADF: return __WASI_EBADF; break; 54 | case ECHILD: return __WASI_ECHILD; break; 55 | case EAGAIN: return __WASI_EAGAIN; break; 56 | case ENOMEM: return __WASI_ENOMEM; break; 57 | case EACCES: return __WASI_EACCES; break; 58 | case EFAULT: return __WASI_EFAULT; break; 59 | case EBUSY: return __WASI_EBUSY; break; 60 | case EEXIST: return __WASI_EEXIST; break; 61 | case EXDEV: return __WASI_EXDEV; break; 62 | case ENODEV: return __WASI_ENODEV; break; 63 | case ENOTDIR: return __WASI_ENOTDIR; break; 64 | case EISDIR: return __WASI_EISDIR; break; 65 | case EINVAL: return __WASI_EINVAL; break; 66 | case ENFILE: return __WASI_ENFILE; break; 67 | case EMFILE: return __WASI_EMFILE; break; 68 | case ENOTTY: return __WASI_ENOTTY; break; 69 | case ETXTBSY: return __WASI_ETXTBSY; break; 70 | case EFBIG: return __WASI_EFBIG; break; 71 | case ENOSPC: return __WASI_ENOSPC; break; 72 | case ESPIPE: return __WASI_ESPIPE; break; 73 | case EROFS: return __WASI_EROFS; break; 74 | case EMLINK: return __WASI_EMLINK; break; 75 | case EPIPE: return __WASI_EPIPE; break; 76 | case EDOM: return __WASI_EDOM; break; 77 | case ERANGE: return __WASI_ERANGE; break; 78 | default: return errno; 79 | } 80 | } 81 | 82 | 83 | void init_wasi(Memory *memory, int argc, char **argv, char **envp) { 84 | // Save memory 85 | wasi_memory = memory; 86 | 87 | // save argc/argv for WASI args_* calls 88 | host_argc = argc; 89 | host_argv = argv; 90 | host_envp = envp; 91 | 92 | // Close dir fds that overlap with where preopen fds need to land (>= 3) 93 | for (int fd = 3; fd < PREOPEN_CNT; fd++) { 94 | if (fcntl(fd, F_GETFD, 0) >= 0) { 95 | close(fd); 96 | } 97 | } 98 | // Open default preopened dirfds 99 | for (int fd = 3; fd < PREOPEN_CNT; fd++) { 100 | int tfd = open(preopen[fd].path, O_RDONLY); 101 | if (tfd < 0) { 102 | FATAL("opening '%s': %s\n", "./", strerror(errno)); 103 | } 104 | if (tfd != fd) { 105 | //char ppath[1024]; 106 | //char path3[1024]; 107 | //sprintf(ppath, "/proc/self/fd/%d", fd); 108 | //int ret = readlink(ppath, path3, 1023); 109 | //FATAL("fd 3 is already open to '%s' (%d)\n", path3, ret); 110 | FATAL("fd %d could not be freed up before preopen\n", fd); 111 | } 112 | } 113 | } 114 | 115 | uint32_t min(uint32_t a, uint32_t b) { 116 | return a <= b ? a : b; 117 | } 118 | 119 | uint32_t addr2offset(void *addr) { 120 | return addr - (void *)wasi_memory->bytes; 121 | } 122 | 123 | void *offset2addr(uint32_t offset) { 124 | return wasi_memory->bytes+offset; 125 | } 126 | 127 | struct iovec *copy_iov_to_host(uint32_t iov_offset, uint32_t iovs_len) { 128 | if (iovs_len > MAX_IOV) { 129 | FATAL("copy_iov_to_host called with iovs_len > 128\n"); 130 | } 131 | 132 | // Convert wasi_memory offsets to host addresses 133 | struct iovec *wasi_iov = offset2addr(iov_offset); 134 | for (int i = 0; i < iovs_len; i++) { 135 | host_iov[i].iov_base = offset2addr((uint32_t)wasi_iov[i].iov_base); 136 | host_iov[i].iov_len = wasi_iov[i].iov_len; 137 | } 138 | return host_iov; 139 | } 140 | 141 | void copy_string_array(char **src, 142 | uint32_t cnt, 143 | char **dst_ptrs, 144 | char *dst_buf, 145 | bool in) { 146 | for (int i = 0; i < cnt; i++) { 147 | dst_ptrs[i] = in ? (char *)addr2offset(dst_buf) : dst_buf; 148 | int alen = strlen(src[i])+1; 149 | memmove(dst_buf, src[i], alen); 150 | dst_buf[alen] = '\0'; 151 | dst_buf += alen; 152 | } 153 | } 154 | 155 | // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-api.md 156 | 157 | uint32_t _wasi_unstable__args_get_(uint32_t argv_offset, 158 | uint32_t argv_buf_offset) { 159 | copy_string_array(host_argv, host_argc, 160 | offset2addr(argv_offset), 161 | offset2addr(argv_buf_offset), 162 | true); 163 | return __WASI_ESUCCESS; 164 | } 165 | 166 | uint32_t _wasi_unstable__args_sizes_get_(uint32_t argc_offset, 167 | uint32_t argv_buf_size_offset) { 168 | size_t *argc = offset2addr(argc_offset); 169 | size_t *argv_buf_size = offset2addr(argv_buf_size_offset); 170 | 171 | *argc = host_argc; 172 | *argv_buf_size = 0; 173 | for (int i = 0; i < host_argc; i++) { 174 | *argv_buf_size += strlen(host_argv[i])+1; 175 | } 176 | return __WASI_ESUCCESS; 177 | } 178 | 179 | uint32_t _wasi_unstable__environ_get_(uint32_t environ_ptrs_offset, 180 | uint32_t environ_strs_offset) { 181 | int environ_count = 0; 182 | while (host_envp[environ_count]) { environ_count += 1; } 183 | 184 | copy_string_array(host_envp, environ_count, 185 | offset2addr(environ_ptrs_offset), 186 | offset2addr(environ_strs_offset), 187 | true); 188 | return __WASI_ESUCCESS; 189 | } 190 | 191 | uint32_t _wasi_unstable__environ_sizes_get_(uint32_t environ_count_offset, 192 | uint32_t environ_buf_size_offset) { 193 | size_t *environ_count = offset2addr(environ_count_offset); 194 | size_t *environ_buf_size = offset2addr(environ_buf_size_offset); 195 | *environ_count = 0; 196 | *environ_buf_size = 0; 197 | while (host_envp[*environ_count]) { 198 | //printf("e: %d -> '%s'\n", *environ_count, host_envp[*environ_count]); 199 | *environ_buf_size += strlen(host_envp[*environ_count])+1; 200 | *environ_count += 1; 201 | } 202 | return __WASI_ESUCCESS; 203 | } 204 | 205 | uint32_t _wasi_unstable__clock_time_get_(uint32_t clock_id, 206 | uint64_t precision, 207 | uint32_t time_offset) { 208 | uint64_t *time = offset2addr(time_offset); 209 | struct timespec tp; 210 | clock_gettime(clock_id, &tp); 211 | *time = (uint64_t)tp.tv_sec * 1000000000 + tp.tv_nsec; 212 | return __WASI_ESUCCESS; 213 | } 214 | 215 | uint32_t _wasi_unstable__fd_close_(uint32_t fd) { 216 | int ret = close(fd); 217 | return ret == 0 ? __WASI_ESUCCESS : ret; 218 | } 219 | 220 | uint32_t _wasi_unstable__fd_datasync_(uint32_t fd) { 221 | int ret = fdatasync(fd); 222 | return ret == 0 ? __WASI_ESUCCESS : ret; 223 | } 224 | 225 | uint32_t _wasi_unstable__fd_fdstat_get_(__wasi_fd_t fd, 226 | uint32_t fdstat_offset) { 227 | struct stat fd_stat; 228 | __wasi_fdstat_t *fdstat = offset2addr(fdstat_offset); 229 | int fl = fcntl(fd, F_GETFL); 230 | if (fl < 0) { return errno_to_wasi(errno); } 231 | fstat(fd, &fd_stat); 232 | int mode = fd_stat.st_mode; 233 | fdstat->fs_filetype = (S_ISBLK(mode) ? __WASI_FILETYPE_BLOCK_DEVICE : 0) | 234 | (S_ISCHR(mode) ? __WASI_FILETYPE_CHARACTER_DEVICE : 0) | 235 | (S_ISDIR(mode) ? __WASI_FILETYPE_DIRECTORY : 0) | 236 | (S_ISREG(mode) ? __WASI_FILETYPE_REGULAR_FILE : 0) | 237 | (S_ISSOCK(mode) ? __WASI_FILETYPE_SOCKET_STREAM : 0) | 238 | (S_ISLNK(mode) ? __WASI_FILETYPE_SYMBOLIC_LINK : 0); 239 | fdstat->fs_flags = ((fl & O_APPEND) ? __WASI_FDFLAG_APPEND : 0) | 240 | ((fl & O_DSYNC) ? __WASI_FDFLAG_DSYNC : 0) | 241 | ((fl & O_NONBLOCK) ? __WASI_FDFLAG_NONBLOCK : 0) | 242 | ((fl & O_RSYNC) ? __WASI_FDFLAG_RSYNC : 0) | 243 | ((fl & O_SYNC) ? __WASI_FDFLAG_SYNC : 0); 244 | fdstat->fs_rights_base = (uint64_t)-1; // all rights 245 | fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights 246 | return __WASI_ESUCCESS; 247 | } 248 | 249 | uint32_t _wasi_unstable__fd_prestat_dir_name_(uint32_t fd, 250 | uint32_t path_offset, 251 | uint32_t path_len) { 252 | if (fd < 3 || fd >= PREOPEN_CNT) { return __WASI_EBADF; } 253 | memmove((char *)offset2addr(path_offset), preopen[fd].path, 254 | min(preopen[fd].path_len, path_len)); 255 | return __WASI_ESUCCESS; 256 | } 257 | 258 | uint32_t _wasi_unstable__fd_prestat_get_(uint32_t fd, 259 | uint32_t buf_offset) { 260 | if (fd < 3 || fd >= PREOPEN_CNT) { return __WASI_EBADF; } 261 | *(uint32_t *)offset2addr(buf_offset) = __WASI_PREOPENTYPE_DIR; 262 | *(uint32_t *)offset2addr(buf_offset+4) = preopen[fd].path_len; 263 | return __WASI_ESUCCESS; 264 | } 265 | 266 | uint32_t _wasi_unstable__fd_read_(__wasi_fd_t fd, 267 | uint32_t iovs_offset, 268 | size_t iovs_len, 269 | uint32_t nread_offset) { 270 | struct iovec *iovs = copy_iov_to_host(iovs_offset, iovs_len); 271 | size_t *nread = offset2addr(nread_offset); 272 | 273 | ssize_t ret = readv(fd, iovs, iovs_len); 274 | if (ret < 0) { return errno_to_wasi(errno); } 275 | *nread = ret; 276 | return __WASI_ESUCCESS; 277 | } 278 | 279 | uint32_t _wasi_unstable__fd_seek_(__wasi_fd_t fd, 280 | __wasi_filedelta_t offset, 281 | __wasi_whence_t whence, 282 | uint32_t newoffset_offset) { 283 | __wasi_filesize_t *filesize = offset2addr(newoffset_offset); 284 | 285 | int wasi_whence = whence == __WASI_WHENCE_END ? SEEK_END : 286 | __WASI_WHENCE_CUR ? SEEK_CUR : 0; 287 | int64_t ret = lseek(fd, offset, wasi_whence); 288 | if (ret < 0) { return errno_to_wasi(errno); } 289 | *filesize = ret; 290 | return __WASI_ESUCCESS; 291 | } 292 | 293 | uint32_t _wasi_unstable__fd_write_(__wasi_fd_t fd, 294 | uint32_t iovs_offset, 295 | size_t iovs_len, 296 | uint32_t nwritten_offset) { 297 | struct iovec *iovs = copy_iov_to_host(iovs_offset, iovs_len); 298 | size_t *nwritten = offset2addr(nwritten_offset); 299 | 300 | ssize_t ret = writev(fd, iovs, iovs_len); 301 | if (ret < 0) { return errno_to_wasi(errno); } 302 | *nwritten = ret; 303 | return __WASI_ESUCCESS; 304 | } 305 | 306 | uint32_t _wasi_unstable__path_open_(__wasi_fd_t dirfd, 307 | __wasi_lookupflags_t dirflags, 308 | uint32_t path_offset, 309 | size_t path_len, 310 | __wasi_oflags_t oflags, 311 | __wasi_rights_t fs_rights_base, 312 | __wasi_rights_t fs_rights_inheriting, 313 | __wasi_fdflags_t fs_flags, 314 | uint32_t fd_offset) { 315 | char *path = offset2addr(path_offset); 316 | __wasi_fd_t *fd = offset2addr(fd_offset); 317 | 318 | // copy path so we can ensure it is NULL terminated 319 | char host_path[1024]; 320 | if (path_len > 1023) { 321 | FATAL("path_open called with path_len > 1023\n"); 322 | } 323 | memmove(host_path, path, path_len); 324 | host_path[path_len] = '\0'; // NULL terminator 325 | 326 | // translate o_flags and fs_flags into flags and mode 327 | int flags = ((oflags & __WASI_O_CREAT) ? O_CREAT : 0) | 328 | ((oflags & __WASI_O_DIRECTORY) ? O_DIRECTORY : 0) | 329 | ((oflags & __WASI_O_EXCL) ? O_EXCL : 0) | 330 | ((oflags & __WASI_O_TRUNC) ? O_TRUNC : 0) | 331 | ((fs_flags & __WASI_FDFLAG_APPEND) ? O_APPEND : 0) | 332 | ((fs_flags & __WASI_FDFLAG_DSYNC) ? O_DSYNC : 0) | 333 | ((fs_flags & __WASI_FDFLAG_NONBLOCK) ? O_NONBLOCK : 0) | 334 | ((fs_flags & __WASI_FDFLAG_RSYNC) ? O_RSYNC : 0) | 335 | ((fs_flags & __WASI_FDFLAG_SYNC) ? O_SYNC : 0); 336 | if ((fs_rights_base & __WASI_RIGHT_FD_READ) && 337 | (fs_rights_base & __WASI_RIGHT_FD_WRITE)) { 338 | flags |= O_RDWR; 339 | } else if ((fs_rights_base & __WASI_RIGHT_FD_WRITE)) { 340 | flags |= O_WRONLY; 341 | } else if ((fs_rights_base & __WASI_RIGHT_FD_READ)) { 342 | flags |= O_RDONLY; // no-op because O_RDONLY is 0 343 | } 344 | int mode = 0644; 345 | int host_fd = openat(dirfd, host_path, flags, mode); 346 | if (host_fd < 0) { return errno_to_wasi(errno); } 347 | 348 | *fd = host_fd; 349 | return __WASI_ESUCCESS; 350 | } 351 | 352 | uint32_t _wasi_unstable__proc_exit_(uint32_t code) { 353 | exit(code); 354 | } 355 | 356 | 357 | -------------------------------------------------------------------------------- /wasi.h: -------------------------------------------------------------------------------- 1 | #ifndef WASI_H 2 | #define WASI_H 3 | 4 | #define __wasi__ 5 | #include "wasi_core.h" 6 | 7 | /////////////////////////////////////////////////////////// 8 | 9 | typedef struct Preopen { 10 | char *path; 11 | int path_len; 12 | } Preopen; 13 | 14 | void init_wasi(Memory *memory, int argc, char **argv, char **envp); 15 | 16 | #endif // WASI_H 17 | -------------------------------------------------------------------------------- /wasi_core.h: -------------------------------------------------------------------------------- 1 | // Pulled from: 2 | // https://raw.githubusercontent.com/CraneStation/wasi-sysroot/wasi/libc-bottom-half/headers/public/wasi/core.h 3 | // On Apr 18, 2019 4 | 5 | /* 6 | * This file describes the WASI interface, consisting of functions, types, 7 | * and defined values (macros). 8 | * 9 | * The interface described here is greatly inspired by [CloudABI]'s clean, 10 | * thoughtfully-designed, cabability-oriented, POSIX-style API. 11 | * 12 | * [CloudABI]: https://github.com/NuxiNL/cloudlibc 13 | */ 14 | 15 | #ifndef __wasi_core_h 16 | #define __wasi_core_h 17 | 18 | #ifndef __wasi__ 19 | #error is only supported on WASI platforms. 20 | #endif 21 | 22 | #include 23 | #include 24 | 25 | _Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout"); 26 | _Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout"); 27 | _Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout"); 28 | _Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout"); 29 | _Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout"); 30 | _Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout"); 31 | //_Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout"); 32 | //_Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout"); 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | typedef uint8_t __wasi_advice_t; 39 | #define __WASI_ADVICE_NORMAL (UINT8_C(0)) 40 | #define __WASI_ADVICE_SEQUENTIAL (UINT8_C(1)) 41 | #define __WASI_ADVICE_RANDOM (UINT8_C(2)) 42 | #define __WASI_ADVICE_WILLNEED (UINT8_C(3)) 43 | #define __WASI_ADVICE_DONTNEED (UINT8_C(4)) 44 | #define __WASI_ADVICE_NOREUSE (UINT8_C(5)) 45 | 46 | typedef uint32_t __wasi_clockid_t; 47 | #define __WASI_CLOCK_REALTIME (UINT32_C(0)) 48 | #define __WASI_CLOCK_MONOTONIC (UINT32_C(1)) 49 | #define __WASI_CLOCK_PROCESS_CPUTIME_ID (UINT32_C(2)) 50 | #define __WASI_CLOCK_THREAD_CPUTIME_ID (UINT32_C(3)) 51 | 52 | typedef uint64_t __wasi_device_t; 53 | 54 | typedef uint64_t __wasi_dircookie_t; 55 | #define __WASI_DIRCOOKIE_START (UINT64_C(0)) 56 | 57 | typedef uint16_t __wasi_errno_t; 58 | #define __WASI_ESUCCESS (UINT16_C(0)) 59 | #define __WASI_E2BIG (UINT16_C(1)) 60 | #define __WASI_EACCES (UINT16_C(2)) 61 | #define __WASI_EADDRINUSE (UINT16_C(3)) 62 | #define __WASI_EADDRNOTAVAIL (UINT16_C(4)) 63 | #define __WASI_EAFNOSUPPORT (UINT16_C(5)) 64 | #define __WASI_EAGAIN (UINT16_C(6)) 65 | #define __WASI_EALREADY (UINT16_C(7)) 66 | #define __WASI_EBADF (UINT16_C(8)) 67 | #define __WASI_EBADMSG (UINT16_C(9)) 68 | #define __WASI_EBUSY (UINT16_C(10)) 69 | #define __WASI_ECANCELED (UINT16_C(11)) 70 | #define __WASI_ECHILD (UINT16_C(12)) 71 | #define __WASI_ECONNABORTED (UINT16_C(13)) 72 | #define __WASI_ECONNREFUSED (UINT16_C(14)) 73 | #define __WASI_ECONNRESET (UINT16_C(15)) 74 | #define __WASI_EDEADLK (UINT16_C(16)) 75 | #define __WASI_EDESTADDRREQ (UINT16_C(17)) 76 | #define __WASI_EDOM (UINT16_C(18)) 77 | #define __WASI_EDQUOT (UINT16_C(19)) 78 | #define __WASI_EEXIST (UINT16_C(20)) 79 | #define __WASI_EFAULT (UINT16_C(21)) 80 | #define __WASI_EFBIG (UINT16_C(22)) 81 | #define __WASI_EHOSTUNREACH (UINT16_C(23)) 82 | #define __WASI_EIDRM (UINT16_C(24)) 83 | #define __WASI_EILSEQ (UINT16_C(25)) 84 | #define __WASI_EINPROGRESS (UINT16_C(26)) 85 | #define __WASI_EINTR (UINT16_C(27)) 86 | #define __WASI_EINVAL (UINT16_C(28)) 87 | #define __WASI_EIO (UINT16_C(29)) 88 | #define __WASI_EISCONN (UINT16_C(30)) 89 | #define __WASI_EISDIR (UINT16_C(31)) 90 | #define __WASI_ELOOP (UINT16_C(32)) 91 | #define __WASI_EMFILE (UINT16_C(33)) 92 | #define __WASI_EMLINK (UINT16_C(34)) 93 | #define __WASI_EMSGSIZE (UINT16_C(35)) 94 | #define __WASI_EMULTIHOP (UINT16_C(36)) 95 | #define __WASI_ENAMETOOLONG (UINT16_C(37)) 96 | #define __WASI_ENETDOWN (UINT16_C(38)) 97 | #define __WASI_ENETRESET (UINT16_C(39)) 98 | #define __WASI_ENETUNREACH (UINT16_C(40)) 99 | #define __WASI_ENFILE (UINT16_C(41)) 100 | #define __WASI_ENOBUFS (UINT16_C(42)) 101 | #define __WASI_ENODEV (UINT16_C(43)) 102 | #define __WASI_ENOENT (UINT16_C(44)) 103 | #define __WASI_ENOEXEC (UINT16_C(45)) 104 | #define __WASI_ENOLCK (UINT16_C(46)) 105 | #define __WASI_ENOLINK (UINT16_C(47)) 106 | #define __WASI_ENOMEM (UINT16_C(48)) 107 | #define __WASI_ENOMSG (UINT16_C(49)) 108 | #define __WASI_ENOPROTOOPT (UINT16_C(50)) 109 | #define __WASI_ENOSPC (UINT16_C(51)) 110 | #define __WASI_ENOSYS (UINT16_C(52)) 111 | #define __WASI_ENOTCONN (UINT16_C(53)) 112 | #define __WASI_ENOTDIR (UINT16_C(54)) 113 | #define __WASI_ENOTEMPTY (UINT16_C(55)) 114 | #define __WASI_ENOTRECOVERABLE (UINT16_C(56)) 115 | #define __WASI_ENOTSOCK (UINT16_C(57)) 116 | #define __WASI_ENOTSUP (UINT16_C(58)) 117 | #define __WASI_ENOTTY (UINT16_C(59)) 118 | #define __WASI_ENXIO (UINT16_C(60)) 119 | #define __WASI_EOVERFLOW (UINT16_C(61)) 120 | #define __WASI_EOWNERDEAD (UINT16_C(62)) 121 | #define __WASI_EPERM (UINT16_C(63)) 122 | #define __WASI_EPIPE (UINT16_C(64)) 123 | #define __WASI_EPROTO (UINT16_C(65)) 124 | #define __WASI_EPROTONOSUPPORT (UINT16_C(66)) 125 | #define __WASI_EPROTOTYPE (UINT16_C(67)) 126 | #define __WASI_ERANGE (UINT16_C(68)) 127 | #define __WASI_EROFS (UINT16_C(69)) 128 | #define __WASI_ESPIPE (UINT16_C(70)) 129 | #define __WASI_ESRCH (UINT16_C(71)) 130 | #define __WASI_ESTALE (UINT16_C(72)) 131 | #define __WASI_ETIMEDOUT (UINT16_C(73)) 132 | #define __WASI_ETXTBSY (UINT16_C(74)) 133 | #define __WASI_EXDEV (UINT16_C(75)) 134 | #define __WASI_ENOTCAPABLE (UINT16_C(76)) 135 | 136 | typedef uint16_t __wasi_eventrwflags_t; 137 | #define __WASI_EVENT_FD_READWRITE_HANGUP (UINT16_C(0x0001)) 138 | 139 | typedef uint8_t __wasi_eventtype_t; 140 | #define __WASI_EVENTTYPE_CLOCK (UINT8_C(0)) 141 | #define __WASI_EVENTTYPE_FD_READ (UINT8_C(1)) 142 | #define __WASI_EVENTTYPE_FD_WRITE (UINT8_C(2)) 143 | 144 | typedef uint32_t __wasi_exitcode_t; 145 | 146 | typedef uint32_t __wasi_fd_t; 147 | 148 | typedef uint16_t __wasi_fdflags_t; 149 | #define __WASI_FDFLAG_APPEND (UINT16_C(0x0001)) 150 | #define __WASI_FDFLAG_DSYNC (UINT16_C(0x0002)) 151 | #define __WASI_FDFLAG_NONBLOCK (UINT16_C(0x0004)) 152 | #define __WASI_FDFLAG_RSYNC (UINT16_C(0x0008)) 153 | #define __WASI_FDFLAG_SYNC (UINT16_C(0x0010)) 154 | 155 | typedef int64_t __wasi_filedelta_t; 156 | 157 | typedef uint64_t __wasi_filesize_t; 158 | 159 | typedef uint8_t __wasi_filetype_t; 160 | #define __WASI_FILETYPE_UNKNOWN (UINT8_C(0)) 161 | #define __WASI_FILETYPE_BLOCK_DEVICE (UINT8_C(1)) 162 | #define __WASI_FILETYPE_CHARACTER_DEVICE (UINT8_C(2)) 163 | #define __WASI_FILETYPE_DIRECTORY (UINT8_C(3)) 164 | #define __WASI_FILETYPE_REGULAR_FILE (UINT8_C(4)) 165 | #define __WASI_FILETYPE_SOCKET_DGRAM (UINT8_C(5)) 166 | #define __WASI_FILETYPE_SOCKET_STREAM (UINT8_C(6)) 167 | #define __WASI_FILETYPE_SYMBOLIC_LINK (UINT8_C(7)) 168 | 169 | typedef uint16_t __wasi_fstflags_t; 170 | #define __WASI_FILESTAT_SET_ATIM (UINT16_C(0x0001)) 171 | #define __WASI_FILESTAT_SET_ATIM_NOW (UINT16_C(0x0002)) 172 | #define __WASI_FILESTAT_SET_MTIM (UINT16_C(0x0004)) 173 | #define __WASI_FILESTAT_SET_MTIM_NOW (UINT16_C(0x0008)) 174 | 175 | typedef uint64_t __wasi_inode_t; 176 | 177 | typedef uint32_t __wasi_linkcount_t; 178 | 179 | typedef uint32_t __wasi_lookupflags_t; 180 | #define __WASI_LOOKUP_SYMLINK_FOLLOW (UINT32_C(0x00000001)) 181 | 182 | typedef uint16_t __wasi_oflags_t; 183 | #define __WASI_O_CREAT (UINT16_C(0x0001)) 184 | #define __WASI_O_DIRECTORY (UINT16_C(0x0002)) 185 | #define __WASI_O_EXCL (UINT16_C(0x0004)) 186 | #define __WASI_O_TRUNC (UINT16_C(0x0008)) 187 | 188 | typedef uint16_t __wasi_riflags_t; 189 | #define __WASI_SOCK_RECV_PEEK (UINT16_C(0x0001)) 190 | #define __WASI_SOCK_RECV_WAITALL (UINT16_C(0x0002)) 191 | 192 | typedef uint64_t __wasi_rights_t; 193 | #define __WASI_RIGHT_FD_DATASYNC (UINT64_C(0x0000000000000001)) 194 | #define __WASI_RIGHT_FD_READ (UINT64_C(0x0000000000000002)) 195 | #define __WASI_RIGHT_FD_SEEK (UINT64_C(0x0000000000000004)) 196 | #define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS (UINT64_C(0x0000000000000008)) 197 | #define __WASI_RIGHT_FD_SYNC (UINT64_C(0x0000000000000010)) 198 | #define __WASI_RIGHT_FD_TELL (UINT64_C(0x0000000000000020)) 199 | #define __WASI_RIGHT_FD_WRITE (UINT64_C(0x0000000000000040)) 200 | #define __WASI_RIGHT_FD_ADVISE (UINT64_C(0x0000000000000080)) 201 | #define __WASI_RIGHT_FD_ALLOCATE (UINT64_C(0x0000000000000100)) 202 | #define __WASI_RIGHT_PATH_CREATE_DIRECTORY (UINT64_C(0x0000000000000200)) 203 | #define __WASI_RIGHT_PATH_CREATE_FILE (UINT64_C(0x0000000000000400)) 204 | #define __WASI_RIGHT_PATH_LINK_SOURCE (UINT64_C(0x0000000000000800)) 205 | #define __WASI_RIGHT_PATH_LINK_TARGET (UINT64_C(0x0000000000001000)) 206 | #define __WASI_RIGHT_PATH_OPEN (UINT64_C(0x0000000000002000)) 207 | #define __WASI_RIGHT_FD_READDIR (UINT64_C(0x0000000000004000)) 208 | #define __WASI_RIGHT_PATH_READLINK (UINT64_C(0x0000000000008000)) 209 | #define __WASI_RIGHT_PATH_RENAME_SOURCE (UINT64_C(0x0000000000010000)) 210 | #define __WASI_RIGHT_PATH_RENAME_TARGET (UINT64_C(0x0000000000020000)) 211 | #define __WASI_RIGHT_PATH_FILESTAT_GET (UINT64_C(0x0000000000040000)) 212 | #define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE (UINT64_C(0x0000000000080000)) 213 | #define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES (UINT64_C(0x0000000000100000)) 214 | #define __WASI_RIGHT_FD_FILESTAT_GET (UINT64_C(0x0000000000200000)) 215 | #define __WASI_RIGHT_FD_FILESTAT_SET_SIZE (UINT64_C(0x0000000000400000)) 216 | #define __WASI_RIGHT_FD_FILESTAT_SET_TIMES (UINT64_C(0x0000000000800000)) 217 | #define __WASI_RIGHT_PATH_SYMLINK (UINT64_C(0x0000000001000000)) 218 | #define __WASI_RIGHT_PATH_REMOVE_DIRECTORY (UINT64_C(0x0000000002000000)) 219 | #define __WASI_RIGHT_PATH_UNLINK_FILE (UINT64_C(0x0000000004000000)) 220 | #define __WASI_RIGHT_POLL_FD_READWRITE (UINT64_C(0x0000000008000000)) 221 | #define __WASI_RIGHT_SOCK_SHUTDOWN (UINT64_C(0x0000000010000000)) 222 | 223 | typedef uint16_t __wasi_roflags_t; 224 | #define __WASI_SOCK_RECV_DATA_TRUNCATED (UINT16_C(0x0001)) 225 | 226 | typedef uint8_t __wasi_sdflags_t; 227 | #define __WASI_SHUT_RD (UINT8_C(0x01)) 228 | #define __WASI_SHUT_WR (UINT8_C(0x02)) 229 | 230 | typedef uint16_t __wasi_siflags_t; 231 | 232 | typedef uint8_t __wasi_signal_t; 233 | /* UINT8_C(0) is reserved; POSIX has special semantics for kill(pid, 0). */ 234 | #define __WASI_SIGHUP (UINT8_C(1)) 235 | #define __WASI_SIGINT (UINT8_C(2)) 236 | #define __WASI_SIGQUIT (UINT8_C(3)) 237 | #define __WASI_SIGILL (UINT8_C(4)) 238 | #define __WASI_SIGTRAP (UINT8_C(5)) 239 | #define __WASI_SIGABRT (UINT8_C(6)) 240 | #define __WASI_SIGBUS (UINT8_C(7)) 241 | #define __WASI_SIGFPE (UINT8_C(8)) 242 | #define __WASI_SIGKILL (UINT8_C(9)) 243 | #define __WASI_SIGUSR1 (UINT8_C(10)) 244 | #define __WASI_SIGSEGV (UINT8_C(11)) 245 | #define __WASI_SIGUSR2 (UINT8_C(12)) 246 | #define __WASI_SIGPIPE (UINT8_C(13)) 247 | #define __WASI_SIGALRM (UINT8_C(14)) 248 | #define __WASI_SIGTERM (UINT8_C(15)) 249 | #define __WASI_SIGCHLD (UINT8_C(16)) 250 | #define __WASI_SIGCONT (UINT8_C(17)) 251 | #define __WASI_SIGSTOP (UINT8_C(18)) 252 | #define __WASI_SIGTSTP (UINT8_C(19)) 253 | #define __WASI_SIGTTIN (UINT8_C(20)) 254 | #define __WASI_SIGTTOU (UINT8_C(21)) 255 | #define __WASI_SIGURG (UINT8_C(22)) 256 | #define __WASI_SIGXCPU (UINT8_C(23)) 257 | #define __WASI_SIGXFSZ (UINT8_C(24)) 258 | #define __WASI_SIGVTALRM (UINT8_C(25)) 259 | #define __WASI_SIGPROF (UINT8_C(26)) 260 | #define __WASI_SIGWINCH (UINT8_C(27)) 261 | #define __WASI_SIGPOLL (UINT8_C(28)) 262 | #define __WASI_SIGPWR (UINT8_C(29)) 263 | #define __WASI_SIGSYS (UINT8_C(30)) 264 | 265 | typedef uint16_t __wasi_subclockflags_t; 266 | #define __WASI_SUBSCRIPTION_CLOCK_ABSTIME (UINT16_C(0x0001)) 267 | 268 | typedef uint64_t __wasi_timestamp_t; 269 | 270 | typedef uint64_t __wasi_userdata_t; 271 | 272 | typedef uint8_t __wasi_whence_t; 273 | #define __WASI_WHENCE_CUR (UINT8_C(0)) 274 | #define __WASI_WHENCE_END (UINT8_C(1)) 275 | #define __WASI_WHENCE_SET (UINT8_C(2)) 276 | 277 | typedef uint8_t __wasi_preopentype_t; 278 | #define __WASI_PREOPENTYPE_DIR (UINT8_C(0)) 279 | 280 | typedef struct __wasi_dirent_t { 281 | __wasi_dircookie_t d_next; 282 | __wasi_inode_t d_ino; 283 | uint32_t d_namlen; 284 | __wasi_filetype_t d_type; 285 | } __wasi_dirent_t; 286 | _Static_assert(offsetof(__wasi_dirent_t, d_next) == 0, "non-wasi data layout"); 287 | _Static_assert(offsetof(__wasi_dirent_t, d_ino) == 8, "non-wasi data layout"); 288 | _Static_assert(offsetof(__wasi_dirent_t, d_namlen) == 16, "non-wasi data layout"); 289 | _Static_assert(offsetof(__wasi_dirent_t, d_type) == 20, "non-wasi data layout"); 290 | _Static_assert(sizeof(__wasi_dirent_t) == 24, "non-wasi data layout"); 291 | //_Static_assert(_Alignof(__wasi_dirent_t) == 8, "non-wasi data layout"); 292 | 293 | typedef struct __wasi_event_t { 294 | __wasi_userdata_t userdata; 295 | __wasi_errno_t error; 296 | __wasi_eventtype_t type; 297 | union __wasi_event_u { 298 | struct __wasi_event_u_fd_readwrite_t { 299 | __wasi_filesize_t nbytes; 300 | __wasi_eventrwflags_t flags; 301 | } fd_readwrite; 302 | } u; 303 | } __wasi_event_t; 304 | _Static_assert(offsetof(__wasi_event_t, userdata) == 0, "non-wasi data layout"); 305 | _Static_assert(offsetof(__wasi_event_t, error) == 8, "non-wasi data layout"); 306 | _Static_assert(offsetof(__wasi_event_t, type) == 10, "non-wasi data layout"); 307 | //_Static_assert( 308 | // offsetof(__wasi_event_t, u.fd_readwrite.nbytes) == 16, "non-wasi data layout"); 309 | //_Static_assert( 310 | // offsetof(__wasi_event_t, u.fd_readwrite.flags) == 24, "non-wasi data layout"); 311 | //_Static_assert(sizeof(__wasi_event_t) == 32, "non-wasi data layout"); 312 | //_Static_assert(_Alignof(__wasi_event_t) == 8, "non-wasi data layout"); 313 | 314 | typedef struct __wasi_prestat_t { 315 | __wasi_preopentype_t pr_type; 316 | union __wasi_prestat_u { 317 | struct __wasi_prestat_u_dir_t { 318 | size_t pr_name_len; 319 | } dir; 320 | } u; 321 | } __wasi_prestat_t; 322 | _Static_assert(offsetof(__wasi_prestat_t, pr_type) == 0, "non-wasi data layout"); 323 | _Static_assert(sizeof(void *) != 4 || 324 | offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 4, "non-wasi data layout"); 325 | _Static_assert(sizeof(void *) != 8 || 326 | offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 8, "non-wasi data layout"); 327 | _Static_assert(sizeof(void *) != 4 || 328 | sizeof(__wasi_prestat_t) == 8, "non-wasi data layout"); 329 | _Static_assert(sizeof(void *) != 8 || 330 | sizeof(__wasi_prestat_t) == 16, "non-wasi data layout"); 331 | _Static_assert(sizeof(void *) != 4 || 332 | _Alignof(__wasi_prestat_t) == 4, "non-wasi data layout"); 333 | _Static_assert(sizeof(void *) != 8 || 334 | _Alignof(__wasi_prestat_t) == 8, "non-wasi data layout"); 335 | 336 | typedef struct __wasi_fdstat_t { 337 | __wasi_filetype_t fs_filetype; 338 | __wasi_fdflags_t fs_flags; 339 | __wasi_rights_t fs_rights_base; 340 | __wasi_rights_t fs_rights_inheriting; 341 | } __wasi_fdstat_t; 342 | _Static_assert( 343 | offsetof(__wasi_fdstat_t, fs_filetype) == 0, "non-wasi data layout"); 344 | _Static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2, "non-wasi data layout"); 345 | //_Static_assert( 346 | // offsetof(__wasi_fdstat_t, fs_rights_base) == 8, "non-wasi data layout"); 347 | //_Static_assert( 348 | // offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, 349 | // "non-wasi data layout"); 350 | //_Static_assert(sizeof(__wasi_fdstat_t) == 24, "non-wasi data layout"); 351 | //_Static_assert(_Alignof(__wasi_fdstat_t) == 8, "non-wasi data layout"); 352 | 353 | typedef struct __wasi_filestat_t { 354 | __wasi_device_t st_dev; 355 | __wasi_inode_t st_ino; 356 | __wasi_filetype_t st_filetype; 357 | __wasi_linkcount_t st_nlink; 358 | __wasi_filesize_t st_size; 359 | __wasi_timestamp_t st_atim; 360 | __wasi_timestamp_t st_mtim; 361 | __wasi_timestamp_t st_ctim; 362 | } __wasi_filestat_t; 363 | _Static_assert(offsetof(__wasi_filestat_t, st_dev) == 0, "non-wasi data layout"); 364 | _Static_assert(offsetof(__wasi_filestat_t, st_ino) == 8, "non-wasi data layout"); 365 | _Static_assert( 366 | offsetof(__wasi_filestat_t, st_filetype) == 16, "non-wasi data layout"); 367 | _Static_assert( 368 | offsetof(__wasi_filestat_t, st_nlink) == 20, "non-wasi data layout"); 369 | _Static_assert( 370 | offsetof(__wasi_filestat_t, st_size) == 24, "non-wasi data layout"); 371 | _Static_assert( 372 | offsetof(__wasi_filestat_t, st_atim) == 32, "non-wasi data layout"); 373 | _Static_assert( 374 | offsetof(__wasi_filestat_t, st_mtim) == 40, "non-wasi data layout"); 375 | _Static_assert( 376 | offsetof(__wasi_filestat_t, st_ctim) == 48, "non-wasi data layout"); 377 | _Static_assert(sizeof(__wasi_filestat_t) == 56, "non-wasi data layout"); 378 | //_Static_assert(_Alignof(__wasi_filestat_t) == 8, "non-wasi data layout"); 379 | 380 | typedef struct __wasi_ciovec_t { 381 | const void *buf; 382 | size_t buf_len; 383 | } __wasi_ciovec_t; 384 | _Static_assert(offsetof(__wasi_ciovec_t, buf) == 0, "non-wasi data layout"); 385 | _Static_assert(sizeof(void *) != 4 || 386 | offsetof(__wasi_ciovec_t, buf_len) == 4, "non-wasi data layout"); 387 | _Static_assert(sizeof(void *) != 8 || 388 | offsetof(__wasi_ciovec_t, buf_len) == 8, "non-wasi data layout"); 389 | _Static_assert(sizeof(void *) != 4 || 390 | sizeof(__wasi_ciovec_t) == 8, "non-wasi data layout"); 391 | _Static_assert(sizeof(void *) != 8 || 392 | sizeof(__wasi_ciovec_t) == 16, "non-wasi data layout"); 393 | _Static_assert(sizeof(void *) != 4 || 394 | _Alignof(__wasi_ciovec_t) == 4, "non-wasi data layout"); 395 | _Static_assert(sizeof(void *) != 8 || 396 | _Alignof(__wasi_ciovec_t) == 8, "non-wasi data layout"); 397 | 398 | typedef struct __wasi_iovec_t { 399 | void *buf; 400 | size_t buf_len; 401 | } __wasi_iovec_t; 402 | _Static_assert(offsetof(__wasi_iovec_t, buf) == 0, "non-wasi data layout"); 403 | _Static_assert(sizeof(void *) != 4 || 404 | offsetof(__wasi_iovec_t, buf_len) == 4, "non-wasi data layout"); 405 | _Static_assert(sizeof(void *) != 8 || 406 | offsetof(__wasi_iovec_t, buf_len) == 8, "non-wasi data layout"); 407 | _Static_assert(sizeof(void *) != 4 || 408 | sizeof(__wasi_iovec_t) == 8, "non-wasi data layout"); 409 | _Static_assert(sizeof(void *) != 8 || 410 | sizeof(__wasi_iovec_t) == 16, "non-wasi data layout"); 411 | _Static_assert(sizeof(void *) != 4 || 412 | _Alignof(__wasi_iovec_t) == 4, "non-wasi data layout"); 413 | _Static_assert(sizeof(void *) != 8 || 414 | _Alignof(__wasi_iovec_t) == 8, "non-wasi data layout"); 415 | 416 | typedef struct __wasi_subscription_t { 417 | __wasi_userdata_t userdata; 418 | __wasi_eventtype_t type; 419 | union __wasi_subscription_u { 420 | struct __wasi_subscription_u_clock_t { 421 | __wasi_userdata_t identifier; 422 | __wasi_clockid_t clock_id; 423 | __wasi_timestamp_t timeout; 424 | __wasi_timestamp_t precision; 425 | __wasi_subclockflags_t flags; 426 | } clock; 427 | struct __wasi_subscription_u_fd_readwrite_t { 428 | __wasi_fd_t fd; 429 | } fd_readwrite; 430 | } u; 431 | } __wasi_subscription_t; 432 | _Static_assert( 433 | offsetof(__wasi_subscription_t, userdata) == 0, "non-wasi data layout"); 434 | _Static_assert( 435 | offsetof(__wasi_subscription_t, type) == 8, "non-wasi data layout"); 436 | //_Static_assert( 437 | // offsetof(__wasi_subscription_t, u.clock.identifier) == 16, 438 | // "non-wasi data layout"); 439 | //_Static_assert( 440 | // offsetof(__wasi_subscription_t, u.clock.clock_id) == 24, 441 | // "non-wasi data layout"); 442 | //_Static_assert( 443 | // offsetof(__wasi_subscription_t, u.clock.timeout) == 32, "non-wasi data layout"); 444 | //_Static_assert( 445 | // offsetof(__wasi_subscription_t, u.clock.precision) == 40, 446 | // "non-wasi data layout"); 447 | //_Static_assert( 448 | // offsetof(__wasi_subscription_t, u.clock.flags) == 48, "non-wasi data layout"); 449 | //_Static_assert( 450 | // offsetof(__wasi_subscription_t, u.fd_readwrite.fd) == 16, 451 | // "non-wasi data layout"); 452 | //_Static_assert(sizeof(__wasi_subscription_t) == 56, "non-wasi data layout"); 453 | //_Static_assert(_Alignof(__wasi_subscription_t) == 8, "non-wasi data layout"); 454 | 455 | /* 456 | #define __WASI_SYSCALL_NAME(name) \ 457 | __attribute__((__import_module__("wasi_unstable"), __import_name__(#name))) 458 | */ 459 | 460 | #define __WASI_SYSCALL_NAME(name) 461 | 462 | __wasi_errno_t __wasi_args_get( 463 | char **argv, 464 | char *argv_buf 465 | ) __WASI_SYSCALL_NAME(args_get) __attribute__((__warn_unused_result__)); 466 | 467 | __wasi_errno_t __wasi_args_sizes_get( 468 | size_t *argc, 469 | size_t *argv_buf_size 470 | ) __WASI_SYSCALL_NAME(args_sizes_get) __attribute__((__warn_unused_result__)); 471 | 472 | __wasi_errno_t __wasi_clock_res_get( 473 | __wasi_clockid_t clock_id, 474 | __wasi_timestamp_t *resolution 475 | ) __WASI_SYSCALL_NAME(clock_res_get) __attribute__((__warn_unused_result__)); 476 | 477 | __wasi_errno_t __wasi_clock_time_get( 478 | __wasi_clockid_t clock_id, 479 | __wasi_timestamp_t precision, 480 | __wasi_timestamp_t *time 481 | ) __WASI_SYSCALL_NAME(clock_time_get) __attribute__((__warn_unused_result__)); 482 | 483 | __wasi_errno_t __wasi_environ_get( 484 | char **environ, 485 | char *environ_buf 486 | ) __WASI_SYSCALL_NAME(environ_get) __attribute__((__warn_unused_result__)); 487 | 488 | __wasi_errno_t __wasi_environ_sizes_get( 489 | size_t *environ_count, 490 | size_t *environ_buf_size 491 | ) __WASI_SYSCALL_NAME(environ_sizes_get) __attribute__((__warn_unused_result__)); 492 | 493 | __wasi_errno_t __wasi_fd_prestat_get( 494 | __wasi_fd_t fd, 495 | __wasi_prestat_t *buf 496 | ) __WASI_SYSCALL_NAME(fd_prestat_get) __attribute__((__warn_unused_result__)); 497 | 498 | __wasi_errno_t __wasi_fd_prestat_dir_name( 499 | __wasi_fd_t fd, 500 | char *path, 501 | size_t path_len 502 | ) __WASI_SYSCALL_NAME(fd_prestat_dir_name) __attribute__((__warn_unused_result__)); 503 | 504 | __wasi_errno_t __wasi_fd_close( 505 | __wasi_fd_t fd 506 | ) __WASI_SYSCALL_NAME(fd_close) __attribute__((__warn_unused_result__)); 507 | 508 | __wasi_errno_t __wasi_fd_datasync( 509 | __wasi_fd_t fd 510 | ) __WASI_SYSCALL_NAME(fd_datasync) __attribute__((__warn_unused_result__)); 511 | 512 | __wasi_errno_t __wasi_fd_pread( 513 | __wasi_fd_t fd, 514 | const __wasi_iovec_t *iovs, 515 | size_t iovs_len, 516 | __wasi_filesize_t offset, 517 | size_t *nread 518 | ) __WASI_SYSCALL_NAME(fd_pread) __attribute__((__warn_unused_result__)); 519 | 520 | __wasi_errno_t __wasi_fd_pwrite( 521 | __wasi_fd_t fd, 522 | const __wasi_ciovec_t *iovs, 523 | size_t iovs_len, 524 | __wasi_filesize_t offset, 525 | size_t *nwritten 526 | ) __WASI_SYSCALL_NAME(fd_pwrite) __attribute__((__warn_unused_result__)); 527 | 528 | __wasi_errno_t __wasi_fd_read( 529 | __wasi_fd_t fd, 530 | const __wasi_iovec_t *iovs, 531 | size_t iovs_len, 532 | size_t *nread 533 | ) __WASI_SYSCALL_NAME(fd_read) __attribute__((__warn_unused_result__)); 534 | 535 | __wasi_errno_t __wasi_fd_renumber( 536 | __wasi_fd_t from, 537 | __wasi_fd_t to 538 | ) __WASI_SYSCALL_NAME(fd_renumber) __attribute__((__warn_unused_result__)); 539 | 540 | __wasi_errno_t __wasi_fd_seek( 541 | __wasi_fd_t fd, 542 | __wasi_filedelta_t offset, 543 | __wasi_whence_t whence, 544 | __wasi_filesize_t *newoffset 545 | ) __WASI_SYSCALL_NAME(fd_seek) __attribute__((__warn_unused_result__)); 546 | 547 | __wasi_errno_t __wasi_fd_tell( 548 | __wasi_fd_t fd, 549 | __wasi_filesize_t *newoffset 550 | ) __WASI_SYSCALL_NAME(fd_tell) __attribute__((__warn_unused_result__)); 551 | 552 | __wasi_errno_t __wasi_fd_fdstat_get( 553 | __wasi_fd_t fd, 554 | __wasi_fdstat_t *buf 555 | ) __WASI_SYSCALL_NAME(fd_fdstat_get) __attribute__((__warn_unused_result__)); 556 | 557 | __wasi_errno_t __wasi_fd_fdstat_set_flags( 558 | __wasi_fd_t fd, 559 | __wasi_fdflags_t flags 560 | ) __WASI_SYSCALL_NAME(fd_fdstat_set_flags) __attribute__((__warn_unused_result__)); 561 | 562 | __wasi_errno_t __wasi_fd_fdstat_set_rights( 563 | __wasi_fd_t fd, 564 | __wasi_rights_t fs_rights_base, 565 | __wasi_rights_t fs_rights_inheriting 566 | ) __WASI_SYSCALL_NAME(fd_fdstat_set_rights) __attribute__((__warn_unused_result__)); 567 | 568 | __wasi_errno_t __wasi_fd_sync( 569 | __wasi_fd_t fd 570 | ) __WASI_SYSCALL_NAME(fd_sync) __attribute__((__warn_unused_result__)); 571 | 572 | __wasi_errno_t __wasi_fd_write( 573 | __wasi_fd_t fd, 574 | const __wasi_ciovec_t *iovs, 575 | size_t iovs_len, 576 | size_t *nwritten 577 | ) __WASI_SYSCALL_NAME(fd_write) __attribute__((__warn_unused_result__)); 578 | 579 | __wasi_errno_t __wasi_fd_advise( 580 | __wasi_fd_t fd, 581 | __wasi_filesize_t offset, 582 | __wasi_filesize_t len, 583 | __wasi_advice_t advice 584 | ) __WASI_SYSCALL_NAME(fd_advise) __attribute__((__warn_unused_result__)); 585 | 586 | __wasi_errno_t __wasi_fd_allocate( 587 | __wasi_fd_t fd, 588 | __wasi_filesize_t offset, 589 | __wasi_filesize_t len 590 | ) __WASI_SYSCALL_NAME(fd_allocate) __attribute__((__warn_unused_result__)); 591 | 592 | __wasi_errno_t __wasi_path_create_directory( 593 | __wasi_fd_t fd, 594 | const char *path, 595 | size_t path_len 596 | ) __WASI_SYSCALL_NAME(path_create_directory) __attribute__((__warn_unused_result__)); 597 | 598 | __wasi_errno_t __wasi_path_link( 599 | __wasi_fd_t old_fd, 600 | __wasi_lookupflags_t old_flags, 601 | const char *old_path, 602 | size_t old_path_len, 603 | __wasi_fd_t new_fd, 604 | const char *new_path, 605 | size_t new_path_len 606 | ) __WASI_SYSCALL_NAME(path_link) __attribute__((__warn_unused_result__)); 607 | 608 | __wasi_errno_t __wasi_path_open( 609 | __wasi_fd_t dirfd, 610 | __wasi_lookupflags_t dirflags, 611 | const char *path, 612 | size_t path_len, 613 | __wasi_oflags_t oflags, 614 | __wasi_rights_t fs_rights_base, 615 | __wasi_rights_t fs_rights_inheriting, 616 | __wasi_fdflags_t fs_flags, 617 | __wasi_fd_t *fd 618 | ) __WASI_SYSCALL_NAME(path_open) __attribute__((__warn_unused_result__)); 619 | 620 | __wasi_errno_t __wasi_fd_readdir( 621 | __wasi_fd_t fd, 622 | void *buf, 623 | size_t buf_len, 624 | __wasi_dircookie_t cookie, 625 | size_t *bufused 626 | ) __WASI_SYSCALL_NAME(fd_readdir) __attribute__((__warn_unused_result__)); 627 | 628 | __wasi_errno_t __wasi_path_readlink( 629 | __wasi_fd_t fd, 630 | const char *path, 631 | size_t path_len, 632 | char *buf, 633 | size_t buf_len, 634 | size_t *bufused 635 | ) __WASI_SYSCALL_NAME(path_readlink) __attribute__((__warn_unused_result__)); 636 | 637 | __wasi_errno_t __wasi_path_rename( 638 | __wasi_fd_t old_fd, 639 | const char *old_path, 640 | size_t old_path_len, 641 | __wasi_fd_t new_fd, 642 | const char *new_path, 643 | size_t new_path_len 644 | ) __WASI_SYSCALL_NAME(path_rename) __attribute__((__warn_unused_result__)); 645 | 646 | __wasi_errno_t __wasi_fd_filestat_get( 647 | __wasi_fd_t fd, 648 | __wasi_filestat_t *buf 649 | ) __WASI_SYSCALL_NAME(fd_filestat_get) __attribute__((__warn_unused_result__)); 650 | 651 | __wasi_errno_t __wasi_fd_filestat_set_times( 652 | __wasi_fd_t fd, 653 | __wasi_timestamp_t st_atim, 654 | __wasi_timestamp_t st_mtim, 655 | __wasi_fstflags_t fstflags 656 | ) __WASI_SYSCALL_NAME(fd_filestat_set_times) __attribute__((__warn_unused_result__)); 657 | 658 | __wasi_errno_t __wasi_fd_filestat_set_size( 659 | __wasi_fd_t fd, 660 | __wasi_filesize_t st_size 661 | ) __WASI_SYSCALL_NAME(fd_filestat_set_size) __attribute__((__warn_unused_result__)); 662 | 663 | __wasi_errno_t __wasi_path_filestat_get( 664 | __wasi_fd_t fd, 665 | __wasi_lookupflags_t flags, 666 | const char *path, 667 | size_t path_len, 668 | __wasi_filestat_t *buf 669 | ) __WASI_SYSCALL_NAME(path_filestat_get) __attribute__((__warn_unused_result__)); 670 | 671 | __wasi_errno_t __wasi_path_filestat_set_times( 672 | __wasi_fd_t fd, 673 | __wasi_lookupflags_t flags, 674 | const char *path, 675 | size_t path_len, 676 | __wasi_timestamp_t st_atim, 677 | __wasi_timestamp_t st_mtim, 678 | __wasi_fstflags_t fstflags 679 | ) __WASI_SYSCALL_NAME(path_filestat_set_times) __attribute__((__warn_unused_result__)); 680 | 681 | __wasi_errno_t __wasi_path_symlink( 682 | const char *old_path, 683 | size_t old_path_len, 684 | __wasi_fd_t fd, 685 | const char *new_path, 686 | size_t new_path_len 687 | ) __WASI_SYSCALL_NAME(path_symlink) __attribute__((__warn_unused_result__)); 688 | 689 | __wasi_errno_t __wasi_path_unlink_file( 690 | __wasi_fd_t fd, 691 | const char *path, 692 | size_t path_len 693 | ) __WASI_SYSCALL_NAME(path_unlink_file) __attribute__((__warn_unused_result__)); 694 | 695 | __wasi_errno_t __wasi_path_remove_directory( 696 | __wasi_fd_t fd, 697 | const char *path, 698 | size_t path_len 699 | ) __WASI_SYSCALL_NAME(path_remove_directory) __attribute__((__warn_unused_result__)); 700 | 701 | __wasi_errno_t __wasi_poll_oneoff( 702 | const __wasi_subscription_t *in, 703 | __wasi_event_t *out, 704 | size_t nsubscriptions, 705 | size_t *nevents 706 | ) __WASI_SYSCALL_NAME(poll_oneoff) __attribute__((__warn_unused_result__)); 707 | 708 | _Noreturn void __wasi_proc_exit( 709 | __wasi_exitcode_t rval 710 | ) __WASI_SYSCALL_NAME(proc_exit); 711 | 712 | __wasi_errno_t __wasi_proc_raise( 713 | __wasi_signal_t sig 714 | ) __WASI_SYSCALL_NAME(proc_raise) __attribute__((__warn_unused_result__)); 715 | 716 | __wasi_errno_t __wasi_random_get( 717 | void *buf, 718 | size_t buf_len 719 | ) __WASI_SYSCALL_NAME(random_get) __attribute__((__warn_unused_result__)); 720 | 721 | __wasi_errno_t __wasi_sock_recv( 722 | __wasi_fd_t sock, 723 | const __wasi_iovec_t *ri_data, 724 | size_t ri_data_len, 725 | __wasi_riflags_t ri_flags, 726 | size_t *ro_datalen, 727 | __wasi_roflags_t *ro_flags 728 | ) __WASI_SYSCALL_NAME(sock_recv) __attribute__((__warn_unused_result__)); 729 | 730 | __wasi_errno_t __wasi_sock_send( 731 | __wasi_fd_t sock, 732 | const __wasi_ciovec_t *si_data, 733 | size_t si_data_len, 734 | __wasi_siflags_t si_flags, 735 | size_t *so_datalen 736 | ) __WASI_SYSCALL_NAME(sock_send) __attribute__((__warn_unused_result__)); 737 | 738 | __wasi_errno_t __wasi_sock_shutdown( 739 | __wasi_fd_t sock, 740 | __wasi_sdflags_t how 741 | ) __WASI_SYSCALL_NAME(sock_shutdown) __attribute__((__warn_unused_result__)); 742 | 743 | __wasi_errno_t __wasi_sched_yield(void) 744 | __WASI_SYSCALL_NAME(sched_yield) __attribute__((__warn_unused_result__)); 745 | 746 | #ifdef __cplusplus 747 | } 748 | #endif 749 | 750 | #undef __WASI_SYSCALL_NAME 751 | 752 | #endif 753 | -------------------------------------------------------------------------------- /wax.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "util.h" 8 | #include "wa.h" 9 | #include "platform.h" 10 | #include "wasi.h" 11 | 12 | void _spectest__print_(uint32_t val) { 13 | //printf("spectest.print 0x%x:i32\n", val); 14 | printf("0x%x:i32\n", val); 15 | } 16 | void _spectest__print_i32_(uint32_t val) { 17 | printf("0x%x:i32\n", val); 18 | } 19 | void _spectest__print_i64_(uint64_t val) { 20 | printf("0x%llx:i64\n", val); 21 | } 22 | 23 | 24 | void usage(char *prog) { 25 | fprintf(stderr, "%s WASM_FILE [ARG...]\n", prog); 26 | exit(2); 27 | } 28 | 29 | ///////////////////////////////////////////////////////// 30 | // Command line 31 | 32 | int main(int argc, char **argv, char **envp) { 33 | char *mod_path; 34 | int res = 0; 35 | uint8_t *bytes = NULL; 36 | int byte_count; 37 | 38 | // Parse arguments 39 | if (argc < 2) { usage(argv[0]); } 40 | mod_path = argv[1]; 41 | 42 | // Load the module 43 | #if PLATFORM==1 44 | // open and mmap the WASM module 45 | bytes = mmap_file(mod_path, &byte_count); 46 | #else 47 | // read the file via mem_fs or serial method 48 | byte_count = vfs_file_size(mod_path); 49 | if (byte_count > 0) { 50 | bytes = malloc(byte_count); 51 | vfs_read_file(mod_path, (char *)bytes); 52 | } 53 | #endif 54 | if (bytes == NULL) { 55 | fprintf(stderr, "Could not load %s", mod_path); 56 | return 2; 57 | } 58 | 59 | Options opts; 60 | Module *m = load_module(bytes, byte_count, opts); 61 | m->path = mod_path; 62 | 63 | Memory *mem = get_export(m, "memory", KIND_MEMORY); 64 | if (!mem) { FATAL("no exported memory named 'memory'\n"); } 65 | init_wasi(mem, argc-1, argv+1, envp); 66 | 67 | // Invoke main/_main function and exit 68 | Block *func = get_export(m, "_start", KIND_FUNCTION); 69 | if ((!func)) { FATAL("no exported function named '_start'\n"); } 70 | res = invoke(m, func->fidx); 71 | 72 | if (!res) { 73 | error("Exception: %s\n", exception); 74 | return 1; 75 | } 76 | 77 | if (m->sp >= 0) { 78 | return (m->stack[m->sp--].value.uint32); 79 | } else { 80 | return 0; 81 | } 82 | 83 | } 84 | --------------------------------------------------------------------------------