Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wizer/include/wizer.h
2459 views
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
135