1 /* 2 * Wizer interface for Wasm module to be initialized. 3 * 4 * This header provides several macros that allow a Wasm module written in C/C++ 5 * to declare an initializer function, and ensure that global constructors (in 6 * C++'s case) are run at initialization time rather than on startup of the 7 * pre-initialized module. 8 */ 9 #ifndef _WIZER_H_ 10 #define _WIZER_H_ 11 12 #ifdef __cplusplus 13 #define __WIZER_EXTERN_C extern "C" 14 #else 15 #define __WIZER_EXTERN_C extern 16 #endif 17 18 #ifdef __clang_major__ 19 // wasi-sdk-16 was the first wasi-sdk version that shipped with a version of 20 // wasi-libc that did not include __original_main. However, wasi-sdk-15 shipped 21 // with clang-14.0.0. To correctly identify the boundary where __original_main 22 // no longer exists, we check for either clang-15+ or specifically clang-14.0.4. 23 // 24 // wasi-sdk-17 ships with clang-15.0.6 25 // wasi-sdk-16 ships with clang-14.0.4 26 #if __clang_major__ >= 15 || \ 27 (__clang_major__ == 14 && __clang_patchlevel__ == 4) 28 #define WIZER_MAIN_VOID __main_void 29 #else 30 #define WIZER_MAIN_VOID __original_main 31 #endif 32 #endif 33 34 // We default to assuming that the compiler is new enough to provide 35 // __main_void. 36 #ifndef WIZER_MAIN_VOID 37 #define WIZER_MAIN_VOID __main_void 38 #endif 39 40 /* 41 * This macro inserts the exported functions necessary to allow Wizer to 42 * pre-initialize a Wasm module. 43 * 44 * To use, simply invoke the macro in exactly one compilation unit (C/C++ file) 45 * that is compiled into the Wasm module to be pre-initialized: 46 * 47 * static void my_init_function() { ... } 48 * 49 * WIZER_INIT(my_init_function); 50 * 51 * (The macro refers to the provided init function, so it must have been defined 52 * or must have a forward declaration at the point the macro is used.) 53 * 54 * The resulting module should be processed by Wizer as follows: 55 * 56 * $ wizer -r _start=wizer.resume -o out.wasm in.wasm 57 * 58 * The result of this will be the following behavior: 59 * 60 * - If the `in.wasm` (the direct compilation output of a program including this 61 * macro invocation) is run directly according to the WASI ABI (i.e., by 62 * invoking `_start`), then nothing changes: global constructors are run, 63 * `main()` is invoked, then global destructors are run. The initialization 64 * function is *not* run in this case. 65 * 66 * - During pre-initialization (i.e., during this `wizer` invocation), global 67 * constructors will run, and then the provided initialization function will 68 * run. The module's memory and global-variable state is then snapshotted and 69 * saved into `out.wasm`. 70 * 71 * All other Wizer restrictions apply (see Wizer documentation for details): 72 * for example, WASI hostcalls may be blocked, depending on options, and 73 * invoking any other imported function will result in an immediate trap 74 * and failure of the Wizer run. 75 * 76 * - If the resulting `out.wasm` is then run using the WASI ABI, the program's 77 * global constructors are *not* re-run. Instead, execution starts directly at 78 * `main()`, using the heap and global-variable state left by the global 79 * constructor and initialization function execution during the Wizer 80 * invocation. 81 * 82 * If no initialization function is needed (i.e., only C++ global constructors 83 * should be run), use `WIZER_DEFAULT_INIT()` instead. 84 */ 85 #define WIZER_INIT(init_func) \ 86 __WIZER_EXTERN_C void __wasm_call_ctors(); \ 87 __WIZER_EXTERN_C void __wasm_call_dtors(); \ 88 __WIZER_EXTERN_C void __wasi_proc_exit(int); \ 89 __WIZER_EXTERN_C int WIZER_MAIN_VOID(); \ 90 /* This function's export name `wizer-initialize` is specially */ \ 91 /* recognized by Wizer. It is the direct entry point for pre-init. */ \ 92 __attribute__((export_name("wizer-initialize"))) void __wizer_initialize() { \ 93 /* `__wasm_call_ctors()` is generated by `wasm-ld` and invokes all */ \ 94 /* of the global constructors. It is safe (and in fact necessary) */ \ 95 /* to manually invoke it here because `wizer-initialize` is the */ \ 96 /* direct entry point, and no libc startup (crt1.o or equivalent) */ \ 97 /* is executed before this code does. */ \ 98 __wasm_call_ctors(); \ 99 /* We now invoke the provided init function before returning. */ \ 100 init_func(); \ 101 } \ 102 /* This function replaces `_start` (the WASI-specified entry point) in */ \ 103 /* the pre-initialized Wasm module. */ \ 104 __attribute__((export_name("wizer.resume"))) void __wizer_resume() { \ 105 /* `__main_void()` is defined by the WASI SDK toolchain due to */ \ 106 /* special semantics in C/C++ for the `main()` function, i.e., ito */ \ 107 /* can either take argc/argv or not. It collects arguments using */ \ 108 /* the appropriate WASI calls and then invokes the user program's */ \ 109 /* `main()`. This may change in the future; when it does, we will */ \ 110 /* coordinate with the WASI-SDK toolchain to implement this entry */ \ 111 /* point in an alternate way. */ \ 112 int r = WIZER_MAIN_VOID(); \ 113 /* Because we are replacing `_start()`, we need to manually invoke */ \ 114 /* destructors as well. */ \ 115 __wasm_call_dtors(); \ 116 /* If main returned non-zero code, call `__wasi_proc_exit`. */ \ 117 if (r != 0) { \ 118 __wasi_proc_exit(r); \ 119 } \ 120 } 121 122 /* 123 * This macro is like `WIZER_INIT()`, but takes no initialization function. 124 * Instead, the pre-initialization phase only executes C++ global constructors 125 * before snapshotting the module state. 126 * 127 * See documentation for `WIZER_INIT()` for more details and usage instructions. 128 */ 129 #define WIZER_DEFAULT_INIT() \ 130 static void __empty_init() {} \ 131 WIZER_INIT(__empty_init) 132 133 #endif // _WIZER_H_ 134