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