1# wasm2c: Convert wasm files to C source and header 2 3`wasm2c` takes a WebAssembly module and produces an equivalent C source and 4header. Some examples: 5 6```sh 7# parse binary file test.wasm and write test.c and test.h 8$ wasm2c test.wasm -o test.c 9 10# parse test.wasm, write test.c and test.h, but ignore the debug names, if any 11$ wasm2c test.wasm --no-debug-names -o test.c 12``` 13 14## Tutorial: .wat -> .wasm -> .c 15 16Let's look at a simple example of a factorial function. 17 18```wasm 19(func (export "fac") (param $x i32) (result i32) 20 (if (result i32) (i32.eq (get_local $x) (i32.const 0)) 21 (then (i32.const 1)) 22 (else 23 (i32.mul (get_local $x) (call 0 (i32.sub (get_local $x) (i32.const 1)))) 24 ) 25 ) 26) 27``` 28 29Save this to `fac.wat`. We can convert this to a `.wasm` file by using the 30`wat2wasm` tool: 31 32```sh 33$ wat2wasm fac.wat -o fac.wasm 34``` 35 36We can then convert it to a C source and header by using the `wasm2c` tool: 37 38```sh 39$ wasm2c fac.wasm -o fac.c 40``` 41 42This generates two files, `fac.c` and `fac.h`. We'll take a closer look at 43these files below, but first let's show a simple example of how to use these 44files. 45 46## Using the generated module 47 48To actually use our fac module, we'll use create a new file, `main.c`, that 49include `fac.h`, initializes the module, and calls `fac`. 50 51`wasm2c` generates a few symbols for us, `init` and `Z_facZ_ii`. `init` 52initializes the module, and `Z_facZ_ii` is our exported `fac` function, but 53[name-mangled](https://en.wikipedia.org/wiki/Name_mangling) to include the 54function signature. 55 56We can define `WASM_RT_MODULE_PREFIX` before including `fac.h` to generate 57these symbols with a prefix, in case we already have a symbol called `init` (or 58even `Z_facZ_ii`!) Note that you'll have to compile `fac.c` with this macro 59too, for this to work. 60 61```c 62#include <stdio.h> 63#include <stdlib.h> 64 65/* Uncomment this to define fac_init and fac_Z_facZ_ii instead. */ 66/* #define WASM_RT_MODULE_PREFIX fac_ */ 67 68#include "fac.h" 69 70int main(int argc, char** argv) { 71 /* Make sure there is at least one command-line argument. */ 72 if (argc < 2) return 1; 73 74 /* Convert the argument from a string to an int. We'll implictly cast the int 75 to a `u32`, which is what `fac` expects. */ 76 u32 x = atoi(argv[1]); 77 78 /* Initialize the fac module. Since we didn't define WASM_RT_MODULE_PREFIX, 79 the initialization function is called `init`. */ 80 init(); 81 82 /* Call `fac`, using the mangled name. */ 83 u32 result = Z_facZ_ii(x); 84 85 /* Print the result. */ 86 printf("fac(%u) -> %u\n", x, result); 87 88 return 0; 89} 90``` 91 92To compile the executable, we need to use `main.c` and the generated `fac.c`. 93We'll also include `wasm-rt-impl.c` which has implementations of the various 94`wasm_rt_*` functions used by `fac.c` and `fac.h`. 95 96```sh 97$ cc -o fac main.c fac.c wasm-rt-impl.c 98``` 99 100Now let's test it out! 101 102```sh 103$ ./fac 1 104fac(1) -> 1 105$ ./fac 5 106fac(5) -> 120 107$ ./fac 10 108fac(10) -> 3628800 109``` 110 111You can take a look at the all of these files in 112[wasm2c/examples/fac](/wasm2c/examples/fac). 113 114## Looking at the generated header, `fac.h` 115 116The generated header file looks something like this: 117 118```c 119#ifndef FAC_H_GENERATED_ 120#define FAC_H_GENERATED_ 121#ifdef __cplusplus 122extern "C" { 123#endif 124 125#ifndef WASM_RT_INCLUDED_ 126#define WASM_RT_INCLUDED_ 127 128... 129 130#endif /* WASM_RT_INCLUDED_ */ 131 132extern void WASM_RT_ADD_PREFIX(init)(void); 133 134/* export: 'fac' */ 135extern u32 (*WASM_RT_ADD_PREFIX(Z_facZ_ii))(u32); 136#ifdef __cplusplus 137} 138#endif 139 140#endif /* FAC_H_GENERATED_ */ 141``` 142 143Let's look at each section. The outer `#ifndef` is standard C boilerplate for a 144header. The `extern "C"` part makes sure to not mangle the symbols if using 145this header in C++. 146 147```c 148#ifndef FAC_H_GENERATED_ 149#define FAC_H_GENERATED_ 150#ifdef __cplusplus 151extern "C" { 152#endif 153 154... 155 156#ifdef __cplusplus 157} 158#endif 159#endif /* FAC_H_GENERATED_ */ 160``` 161 162This `WASM_RT_INCLUDED_` section contains a number of definitions required for 163all WebAssembly modules. 164 165```c 166#ifndef WASM_RT_INCLUDED_ 167#define WASM_RT_INCLUDED_ 168 169... 170 171#endif /* WASM_RT_INCLUDED_ */ 172``` 173 174First we can specify the maximum call depth before trapping. This defaults to 175500: 176 177```c 178#ifndef WASM_RT_MAX_CALL_STACK_DEPTH 179#define WASM_RT_MAX_CALL_STACK_DEPTH 500 180#endif 181``` 182 183Next we can specify a module prefix. This is useful if you are using multiple 184modules that may use the same name as an export. Since we only have one module 185here, it's fine to use the default which is an empty prefix: 186 187```c 188#ifndef WASM_RT_MODULE_PREFIX 189#define WASM_RT_MODULE_PREFIX 190#endif 191 192#define WASM_RT_PASTE_(x, y) x ## y 193#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y) 194#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x) 195``` 196 197Next are some convenient typedefs for integers and floats of fixed sizes: 198 199```c 200typedef uint8_t u8; 201typedef int8_t s8; 202typedef uint16_t u16; 203typedef int16_t s16; 204typedef uint32_t u32; 205typedef int32_t s32; 206typedef uint64_t u64; 207typedef int64_t s64; 208typedef float f32; 209typedef double f64; 210``` 211 212Next is the `wasm_rt_trap_t` enum, which is used to give the reason a trap 213occurred. 214 215```c 216typedef enum { 217 WASM_RT_TRAP_NONE, 218 WASM_RT_TRAP_OOB, 219 WASM_RT_TRAP_INT_OVERFLOW, 220 WASM_RT_TRAP_DIV_BY_ZERO, 221 WASM_RT_TRAP_INVALID_CONVERSION, 222 WASM_RT_TRAP_UNREACHABLE, 223 WASM_RT_TRAP_CALL_INDIRECT, 224 WASM_RT_TRAP_EXHAUSTION, 225} wasm_rt_trap_t; 226``` 227 228Next is the `wasm_rt_type_t` enum, which is used for specifying function 229signatures. The four WebAssembly value types are included: 230 231```c 232typedef enum { 233 WASM_RT_I32, 234 WASM_RT_I64, 235 WASM_RT_F32, 236 WASM_RT_F64, 237} wasm_rt_type_t; 238``` 239 240Next is `wasm_rt_anyfunc_t`, the function signature for a generic function 241callback. Since a WebAssembly table can contain functions of any given 242signature, it is necessary to convert them to a canonical form: 243 244```c 245typedef void (*wasm_rt_anyfunc_t)(void); 246``` 247 248Next are the definitions for a table element. `func_type` is a function index 249as returned by `wasm_rt_register_func_type` described below. 250 251```c 252typedef struct { 253 uint32_t func_type; 254 wasm_rt_anyfunc_t func; 255} wasm_rt_elem_t; 256``` 257 258Next is the definition of a memory instance. The `data` field is a pointer to 259`size` bytes of linear memory. The `size` field of `wasm_rt_memory_t` is the 260current size of the memory instance in bytes, whereas `pages` is the current 261size in pages (65536 bytes.) `max_pages` is the maximum number of pages as 262specified by the module, or `0xffffffff` if there is no limit. 263 264```c 265typedef struct { 266 uint8_t* data; 267 uint32_t pages, max_pages; 268 uint32_t size; 269} wasm_rt_memory_t; 270``` 271 272Next is the definition of a table instance. The `data` field is a pointer to 273`size` elements. Like a memory instance, `size` is the current size of a table, 274and `max_size` is the maximum size of the table, or `0xffffffff` if there is no 275limit. 276 277```c 278typedef struct { 279 wasm_rt_elem_t* data; 280 uint32_t max_size; 281 uint32_t size; 282} wasm_rt_table_t; 283``` 284 285## Symbols that must be defined by the embedder 286 287Next in `fac.h` are a collection of extern symbols that must be implemented by 288the embedder (i.e. you) before this C source can be used. 289 290A C implementation of these functions is defined in 291[`wasm-rt-impl.h`](wasm-rt-impl.h) and [`wasm-rt-impl.c`](wasm-rt-impl.c). 292 293```c 294extern void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn)); 295extern uint32_t wasm_rt_register_func_type(uint32_t params, uint32_t results, ...); 296extern void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages); 297extern uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages); 298extern void wasm_rt_allocate_table(wasm_rt_table_t*, uint32_t elements, uint32_t max_elements); 299extern uint32_t wasm_rt_call_stack_depth; 300``` 301 302`wasm_rt_trap` is a function that is called when the module traps. Some 303possible implementations are to throw a C++ exception, or to just abort the 304program execution. 305 306`wasm_rt_register_func_type` is a function that registers a function type. It 307is a variadic function where the first two arguments give the number of 308parameters and results, and the following arguments are the types. For example, 309the function `func (param i32 f32) (result f64)` would register the function 310type as 311`wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_F64)`. 312 313`wasm_rt_allocate_memory` initializes a memory instance, and allocates at least 314enough space for the given number of initial pages. The memory must be cleared 315to zero. 316 317`wasm_rt_grow_memory` must grow the given memory instance by the given number 318of pages. If there isn't enough memory to do so, or the new page count would be 319greater than the maximum page count, the function must fail by returning 320`0xffffffff`. If the function succeeds, it must return the previous size of the 321memory instance, in pages. 322 323`wasm_rt_allocate_table` initializes a table instance, and allocates at least 324enough space for the given number of initial elements. The elements must be 325cleared to zero. 326 327`wasm_rt_call_stack_depth` is the current stack call depth. Since this is 328shared between modules, it must be defined only once, by the embedder. 329 330## Exported symbols 331 332Finally, `fac.h` defines exported symbols provided by the module. In our 333example, the only function we exported was `fac`. An additional function is 334provided called `init`, which initializes the module and must be called before 335the module can be used: 336 337```c 338extern void WASM_RT_ADD_PREFIX(init)(void); 339 340/* export: 'fac' */ 341extern u32 (*WASM_RT_ADD_PREFIX(Z_facZ_ii))(u32); 342``` 343 344All exported names use `WASM_RT_ADD_PREFIX` (as described above) to allow the 345symbols to placed in a namespace as decided by the embedder. All symbols are 346also mangled so they include the types of the function signature. 347 348In our example, `Z_facZ_ii` is the mangling for a function named `fac` that 349takes one `i32` parameter and returns one `i32` result. 350 351## A quick look at `fac.c` 352 353The contents of `fac.c` are internals, but it is useful to see a little about 354how it works. 355 356The first few hundred lines define macros that are used to implement the 357various WebAssembly instructions. Their implementations may be interesting to 358the curious reader, but are out of scope for this document. 359 360Following those definitions are various initialization functions (`init`, 361`init_func_types`, `init_globals`, `init_memory`, `init_table`, and 362`init_exports`.) In our example, most of these functions are empty, since the 363module doesn't use any globals, memory or tables. 364 365The most interesting part is the definition of the function `fac`: 366 367```c 368static u32 fac(u32 p0) { 369 FUNC_PROLOGUE; 370 u32 i0, i1, i2; 371 i0 = p0; 372 i1 = 0u; 373 i0 = i0 == i1; 374 if (i0) { 375 i0 = 1u; 376 } else { 377 i0 = p0; 378 i1 = p0; 379 i2 = 1u; 380 i1 -= i2; 381 i1 = fac(i1); 382 i0 *= i1; 383 } 384 FUNC_EPILOGUE; 385 return i0; 386} 387``` 388 389If you look at the original WebAssembly text in the flat format, you can see 390that there is a 1-1 mapping in the output: 391 392```wasm 393(func $fac (param $x i32) (result i32) 394 get_local $x 395 i32.const 0 396 i32.eq 397 if (result i32) 398 i32.const 1 399 else 400 get_local $x 401 get_local $x 402 i32.const 1 403 i32.sub 404 call 0 405 i32.mul 406 end) 407``` 408 409This looks different than the factorial function above because it is using the 410"flat format" instead of the "folded format". You can use `wat-desugar` to 411convert between the two to be sure: 412 413```sh 414$ wat-desugar fac-flat.wat --fold -o fac-folded.wat 415``` 416 417```wasm 418(module 419 (func (;0;) (param i32) (result i32) 420 (if (result i32) ;; label = @1 421 (i32.eq 422 (get_local 0) 423 (i32.const 0)) 424 (then 425 (i32.const 1)) 426 (else 427 (i32.mul 428 (get_local 0) 429 (call 0 430 (i32.sub 431 (get_local 0) 432 (i32.const 1))))))) 433 (export "fac" (func 0)) 434 (type (;0;) (func (param i32) (result i32)))) 435``` 436 437The formatting is different and the variable and function names are gone, but 438the structure is the same. 439