Skip to content

Commit f3e8728

Browse files
authored
Configure a new stack in cabi_realloc (#854)
This commit is an update to the `cabi_realloc` export configured by wasi-libc, specifically for wasip3 targets. The change here is to change the entrypoint from a C function to a small assembly wrapper which configures the stack pointer and TLS base before calling into C. This is to account for the spec change in WebAssembly/component-model#680 where `realloc` hooks are now going to be invoked with context slots set to 0. This means that `cabi_realloc` runs with no stack, and this would quickly abort in wasi-libc otherwise. The change here is to, when the ABI is using `context` slots for the stack pointer, have an assembly wrapper that configures a known-good stack and TLS block for `cabi_realloc` to use. Once configured the normal C-based `cabi_realloc` routine runs. This is tested against bytecodealliance/wasmtime#13949 to ensure it works on the p3 target with/without coop threading and before/after that Wasmtime change.
1 parent 0d5332a commit f3e8728

14 files changed

Lines changed: 192 additions & 54 deletions

cmake/bindings.cmake

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ if(WIT_BINDGEN_EXECUTABLE)
1515
OUTPUT_VARIABLE WIT_BINDGEN_VERSION
1616
OUTPUT_STRIP_TRAILING_WHITESPACE)
1717

18-
if (NOT (WIT_BINDGEN_VERSION MATCHES "0\\.59\\.0"))
19-
message(WARNING "wit-bindgen version 0.59.0 is required, found: ${WIT_BINDGEN_VERSION}")
18+
if (NOT (WIT_BINDGEN_VERSION MATCHES "0\\.60\\.0"))
19+
message(WARNING "wit-bindgen version 0.60.0 is required, found: ${WIT_BINDGEN_VERSION}")
2020
set(WIT_BINDGEN_EXECUTABLE "")
2121
endif()
2222
endif()
@@ -26,7 +26,7 @@ if (NOT WIT_BINDGEN_EXECUTABLE)
2626
ba_download(
2727
wit-bindgen
2828
"https://github.com/bytecodealliance/wit-bindgen"
29-
"0.59.0"
29+
"0.60.0"
3030
)
3131
ExternalProject_Get_Property(wit-bindgen SOURCE_DIR)
3232
set(wit_bindgen "${SOURCE_DIR}/wit-bindgen")
@@ -152,6 +152,7 @@ function(wit_bindgen_edit p)
152152
COMMAND sed ${SED_INPLACE_ARGS} "'s_#include .wasi${p}\.h._#include \"wasi/wasi${p}.h\"_'" ${bottom_half}/sources/wasi${p}.c
153153
COMMAND sed ${SED_INPLACE_ARGS} "s/extern void exit_exit/_Noreturn extern void exit_exit/" ${bottom_half}/headers/public/wasi/__generated_wasi${p}.h
154154
COMMAND sed ${SED_INPLACE_ARGS} "s/extern void __wasm_import_exit_exit/_Noreturn extern void __wasm_import_exit_exit/" ${bottom_half}/sources/wasi${p}.c
155+
COMMAND sed ${SED_INPLACE_ARGS} "s/__attribute__.*\"cabi_realloc\".*/#include \\\"cabi_realloc_augment.h\\\"/" ${bottom_half}/sources/wasi${p}.c
155156
DEPENDS bindings-${p}
156157
)
157158
add_dependencies(bindings bindings-${p}-edit)

cmake/scripts/run-check-symbols.cmake

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ foreach(line IN LISTS defined_symbols)
4848
# Skip internal debug-only asserts
4949
if (symbol MATCHES "__wasilibc_assert_.*" OR
5050
symbol MATCHES "__wasm_(g|s)et_(stack_pointer|tls_base)" OR
51-
symbol MATCHES "__wasm_init.*")
51+
symbol MATCHES "__wasm_init.*" OR
52+
symbol MATCHES "__cabi_realloc_impl")
5253
continue()
5354
endif()
5455
list(APPEND final_defined_symbols ${symbol})
@@ -64,7 +65,8 @@ foreach(line IN LISTS undefined_symbols)
6465
# Skip internal debug-only asserts
6566
if (symbol MATCHES "__wasilibc_assert_.*" OR
6667
symbol MATCHES "__wasm_(g|s)et_(stack_pointer|tls_base)" OR
67-
symbol MATCHES "__wasm_init.*")
68+
symbol MATCHES "__wasm_init.*" OR
69+
symbol MATCHES "__cabi_realloc_impl")
6870
continue()
6971
endif()
7072
list(APPEND final_undefined_symbols ${symbol})

libc-bottom-half/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ set(bottom_half_sources
135135
sources/reallocarray.c
136136
sources/sbrk.c
137137
sources/truncate.c
138+
sources/__cabi_realloc_wrapper.S
138139
)
139140

140141
if (WASI STREQUAL "p1")

libc-bottom-half/crt/crt1-command.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ extern int __main_void(void);
1010
extern void __wasm_call_dtors(void);
1111

1212
#ifdef __wasm_libcall_thread_context__
13-
// Force __wasm_init_task and __wasm_init_async_task to be linked in for wasip3
13+
// Force some symbols to be linked in for wasip3
1414
extern void __wasm_init_task(void);
1515
extern void __wasm_init_async_task(void);
16+
extern void cabi_realloc(void);
1617
__attribute__((used)) void *__wasm_init_task_ref = __wasm_init_task;
1718
__attribute__((used)) void *__wasm_init_async_task_ref = __wasm_init_async_task;
19+
__attribute__((used)) static void *cabi_realloc_ref = cabi_realloc;
1820
#endif
1921

2022
#if defined(__wasip1__)

libc-bottom-half/crt/crt1-reactor.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
extern void __wasm_call_ctors(void);
55

66
#ifdef __wasm_libcall_thread_context__
7-
// Force __wasm_init_task and __wasm_init_async_task to be linked in for wasip3
7+
// Force some symbols to be linked in for wasip3
88
extern void __wasm_init_task(void);
99
extern void __wasm_init_async_task(void);
10+
extern void cabi_realloc(void);
1011
__attribute__((used)) static void *__wasm_init_task_ref = __wasm_init_task;
1112
__attribute__((used)) static void *__wasm_init_async_task_ref =
1213
__wasm_init_async_task;
14+
__attribute__((used)) static void *cabi_realloc_ref = cabi_realloc;
1315
#endif
1416

1517
__attribute__((export_name("_initialize"))) void _initialize(void) {

libc-bottom-half/headers/public/wasi/__generated_wasip2.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libc-bottom-half/headers/public/wasi/__generated_wasip3.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include <wasi/version.h>
2+
3+
#ifdef __wasm_libcall_thread_context__
4+
// The size, in bytes, of the stack statically allocated for `cabi_realloc`.
5+
// This doesn't need to be too big as it's just running `cabi_realloc` as defined
6+
// in wasi-libc. The current number is determined by:
7+
//
8+
// * Start with 16 bytes.
9+
// * Double until tests don't crash in a debug build.
10+
// * Double again.
11+
//
12+
// The goal is to be pretty modest since this is part of every module.
13+
#define REALLOC_STACK_SIZE 512
14+
15+
// Declare the `__wasm_{g,s}et_stack_pointer` functions.
16+
.functype __wasm_set_stack_pointer (i32) -> ()
17+
.functype __wasm_get_stack_pointer () -> (i32)
18+
.type __wasm_set_stack_pointer,@function
19+
.type __wasm_get_stack_pointer,@function
20+
.functype __cabi_realloc_impl (i32, i32, i32, i32) -> (i32)
21+
.type __cabi_realloc_impl,@function
22+
23+
// Go ahead and generate an `-fPIC`-compatible addressing mode for
24+
// `cabi_realloc_stack` so this is compatible with both PIC and not builds.
25+
.type __memory_base,@global
26+
.globaltype __memory_base, i32, immutable
27+
28+
// For coop-threads builds we need to configure TLS to the initial TLS
29+
// value for all tasks/etc, so import those symbols here.
30+
#ifdef __wasi_cooperative_threads__
31+
.functype __wasm_set_tls_base (i32) -> ()
32+
.functype __wasm_get_tls_base () -> (i32)
33+
.type __wasm_set_tls_base,@function
34+
.type __wasm_get_tls_base,@function
35+
.type __init_tls_base,@global
36+
#ifdef __PIC__
37+
.globaltype __init_tls_base, i32
38+
#else
39+
.globaltype __init_tls_base, i32, immutable
40+
#endif
41+
#endif
42+
43+
// This wrapper takes over the `cabi_realloc` symbol itself and the
44+
// `cabi_realloc` core module export. The C implementation it forwards to is
45+
// renamed to `__cabi_realloc_impl` by `cabi_realloc_augment.h`.
46+
//
47+
// Note that it's important that the symbol name matches the export name here.
48+
// In a `BUILD_SHARED=ON` build `libc.so` is dynamically linked against by other
49+
// shared libraries and by `crt1-{command,reactor}.o`, and the name a symbol is
50+
// resolved by in that context is its wasm export name. If `.export_name` were
51+
// used to rename the export then this function would be unresolvable by name
52+
// from anything linking against `libc.so`.
53+
.globl cabi_realloc
54+
.export_name cabi_realloc, "cabi_realloc"
55+
.type cabi_realloc,@function
56+
cabi_realloc:
57+
.functype cabi_realloc (i32, i32, i32, i32) -> (i32)
58+
59+
// Prior to bytecodealliance/wasmtime#13949 wasmtime's handling of
60+
// `cabi_realloc` and context slots was a bit buggy. That means that prior
61+
// to that PR the slots aren't guaranteed to be 0 and the value present will
62+
// be used by some future task. This export is going to clobber these
63+
// slots, so while that Wasmtime change is percolating this preserves the
64+
// slots around the invocation of `cabi_realloc`. Once #13949 percolates and
65+
// ships then this workaround code, and the restore down below, can be
66+
// deleted.
67+
.local i32, i32
68+
call __wasm_get_stack_pointer
69+
local.set 4
70+
#ifdef __wasi_cooperative_threads__
71+
call __wasm_get_tls_base
72+
local.set 5
73+
#endif
74+
75+
// First up, configure the stack pointer. Get the base of the stack from the
76+
// data symbol defined below, add the stack size, and that's what we're
77+
// starting with.
78+
i32.const cabi_realloc_stack@MBREL
79+
global.get __memory_base
80+
i32.add
81+
i32.const REALLOC_STACK_SIZE
82+
i32.add
83+
call __wasm_set_stack_pointer
84+
85+
// Next up configure TLS. This is only required in coop-threads builds.
86+
#ifdef __wasi_cooperative_threads__
87+
global.get __init_tls_base
88+
call __wasm_set_tls_base
89+
#endif
90+
91+
// And finally forward to the actual implementation of `cabi_realloc`
92+
// defined in `wasip3.c`
93+
local.get 0
94+
local.get 1
95+
local.get 2
96+
local.get 3
97+
call __cabi_realloc_impl
98+
99+
local.get 4
100+
call __wasm_set_stack_pointer
101+
#ifdef __wasi_cooperative_threads__
102+
local.get 5
103+
call __wasm_set_tls_base
104+
#endif
105+
106+
end_function
107+
108+
// Define a data symbol which is the stack that `cabi_realloc` runs on.
109+
// Note that this is aligned to 16-bytes, the expected stack alignment.
110+
.type cabi_realloc_stack,@object
111+
.section .bss.cabi_realloc_stack,"",@
112+
.p2align 4, 0x0
113+
cabi_realloc_stack:
114+
.skip REALLOC_STACK_SIZE
115+
.size cabi_realloc_stack, REALLOC_STACK_SIZE
116+
117+
#endif // __wasm_libcall_thread_context__

libc-bottom-half/sources/__wasm_init_task.S

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,59 @@
22

33
.text
44

5-
.globaltype __init_stack_pointer, i32, immutable
6-
.functype __wasm_set_stack_pointer (i32) -> ()
7-
.globl __wasm_set_stack_pointer
5+
.globaltype __init_stack_pointer, i32, immutable
6+
.functype __wasm_set_stack_pointer (i32) -> ()
7+
.globl __wasm_set_stack_pointer
88

99
#ifdef __wasi_cooperative_threads__
1010
#ifdef __PIC__
11-
.globaltype __init_tls_base, i32
11+
.globaltype __init_tls_base, i32
1212
#else
13-
.globaltype __init_tls_base, i32, immutable
13+
.globaltype __init_tls_base, i32, immutable
1414
#endif
15-
.functype __wasm_set_tls_base (i32) -> ()
16-
.functype __wasilibc_init_async_task () -> (i32)
17-
.functype __wasi_init_tp () -> ()
15+
.functype __wasm_set_tls_base (i32) -> ()
1816
#endif
1917

20-
.globl __wasm_init_task
21-
.export_name __wasm_init_task, __wasm_init_task
22-
.type __wasm_init_task,@function
23-
24-
.globl __wasm_init_async_task
25-
.type __wasm_init_async_task,@function
26-
.export_name __wasm_init_async_task, __wasm_init_async_task
2718

19+
// Entrypoint for initializing both async and sync tasks in the component model.
20+
//
21+
// Calls to this function are injected by `wit-component` when a component is
22+
// created from a core module. Specifically for lifted functions this
23+
// initialization hook is invoked first before the user-specified lifted
24+
// function. The purpose of this function is to configure this task's stack/TLS
25+
// setup and its `context.{get,set}` slots.
26+
//
27+
// For both sync and async tasks once they're entered in this component it's
28+
// guaranteed that no other task will start before they have returned. That
29+
// means that it's safe to use the same stack for all tasks. This does mean that
30+
// the same TLS values are used across all tasks, which seem reasonable-enough
31+
// for now.
32+
//
33+
// Note that configuring TLS is only required in coop threads builds since when
34+
// coop-threads are disabled TLS is routed through a global in wasi-libc which
35+
// needs no extra handling (unlike coop threads where TLS routes through
36+
// `context.{get,set}` which needs setting up).
37+
.globl __wasm_init_task
38+
.export_name __wasm_init_task, __wasm_init_task
39+
.type __wasm_init_task,@function
2840
__wasm_init_task:
2941
.functype __wasm_init_task () -> ()
3042

31-
// Synchronous tasks will return before anything else can enter this
32-
// component, so use the initial stack for this task.
3343
global.get __init_stack_pointer
3444
call __wasm_set_stack_pointer
3545

3646
#ifdef __wasi_cooperative_threads__
37-
// For synchronous tasks in coop threads they inherit the same TLS as the
38-
// original "main thread" that started the module.
3947
global.get __init_tls_base
4048
call __wasm_set_tls_base
4149
#endif
42-
4350
end_function
4451

52+
// This is just a small wrapper around `__wasm_init_task`. Work needs to be done
53+
// in `wit-component` to make this optional.
54+
.globl __wasm_init_async_task
55+
.type __wasm_init_async_task,@function
56+
.export_name __wasm_init_async_task, __wasm_init_async_task
4557
__wasm_init_async_task:
4658
.functype __wasm_init_async_task () -> ()
47-
48-
// Setup a temporary stack for this task to invoke `malloc` on. Note that if
49-
// threading is disabled then this is the actual stack of this task since no
50-
// other tasks can enter while this is blocked and/or actively running.
51-
global.get __init_stack_pointer
52-
call __wasm_set_stack_pointer
53-
54-
#ifdef __wasi_cooperative_threads__
55-
// Setup the initial TLS from the "main thread" in case `malloc` uses TLS.
56-
// This won't be the TLS used by this task, however, in the long run.
57-
global.get __init_tls_base
58-
call __wasm_set_tls_base
59-
60-
// Defer to the C code in wasi-libc to finish initialization. This'll
61-
// allocate a new stack, setup this thread's TLS block, etc. The return
62-
// value of the function is what to use a the stack pointer for the rest of
63-
// this async task.
64-
call __wasilibc_init_async_task
65-
call __wasm_set_stack_pointer
66-
#endif
67-
59+
call __wasm_init_task
6860
end_function
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This is a "surgical" augmentation for the generated `wasip{2,3}.c` files in
2+
// wasi-libc which changes the behavior of the generated `cabi_realloc`. Ideally
3+
// `wit-bindgen` would have a mode that doesn't generate `cabi_realloc`, but that
4+
// doesn't exist right now so we're left to augment it.
5+
//
6+
// Specifically the change made here is that when
7+
// `__wasm_libcall_thread_context__` is enabled, such as on wasip3 targets, the
8+
// actual `cabi_realloc` entrypoint lives in an extern assembly file and the
9+
// `cabi_realloc` symbol in the `wasip3.c` file is renamed to something else.
10+
// This handles how `cabi_realloc` starts with context slots 0'd out and with
11+
// a libcall thread context that means the stack needs to be configured.
12+
//
13+
// To make bindings generation a bit easier this file is `#include`'d in a a
14+
// single location hence the trailing `__attribute__` at the end which is
15+
// applicable when `__wasm_libcall_thread_context__` is disabled.
16+
17+
#ifdef __wasm_libcall_thread_context__
18+
#define cabi_realloc __cabi_realloc_impl
19+
#else
20+
__attribute__((__weak__, __export_name__("cabi_realloc")))
21+
#endif

0 commit comments

Comments
 (0)