Skip to content

Commit f377d3e

Browse files
authored
Stop using dynCalls in wasm2c (#12070)
Instead, this calls the Table directly. This takes a little more effort at compile time because we need to find the index of the function type for the signature we are calling with (so that we trap if the target function has the wrong type). To do that, scan the wasm2c output - which is at risk of breaking if that output changes, sadly (another option might be to disassemble the wasm and scan that, which would at least be a stable format - but this seems fine for now). Helps #12059 Fixes #12065
1 parent d7ef8f3 commit f377d3e

File tree

2 files changed

+108
-70
lines changed

2 files changed

+108
-70
lines changed

tools/wasm2c.py

Lines changed: 108 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,70 @@
99
from tools.shared import Settings, path_from_root, unsuffixed, NODE_JS, check_call, exit_with_error
1010

1111

12+
# map an emscripten-style signature letter to a wasm2c C type
13+
def s_to_c(s):
14+
if s == 'v':
15+
return 'void'
16+
elif s == 'i':
17+
return 'u32'
18+
elif s == 'j':
19+
return 'u64'
20+
elif s == 'f':
21+
return 'f32'
22+
elif s == 'd':
23+
return 'f64'
24+
else:
25+
exit_with_error('invalid sig element:' + str(s))
26+
27+
28+
# map a wasm2c C type to an emscripten-style signature letter
29+
def c_to_s(c):
30+
if c == 'WASM_RT_I32':
31+
return 'i'
32+
elif c == 'WASM_RT_I64':
33+
return 'j'
34+
elif c == 'WASM_RT_F32':
35+
return 'f'
36+
elif c == 'WASM_RT_F64':
37+
return 'd'
38+
else:
39+
exit_with_error('invalid wasm2c type element:' + str(c))
40+
41+
42+
def get_func_types(code):
43+
'''
44+
We look for this pattern:
45+
46+
static void init_func_types(void) {
47+
func_types[0] = wasm_rt_register_func_type(3, 1, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32);
48+
func_types[1] = wasm_rt_register_func_type(1, 1, WASM_RT_I32, WASM_RT_I32);
49+
func_types[2] = wasm_rt_register_func_type(0, 0);
50+
}
51+
52+
We return a map of signatures names to their index.
53+
'''
54+
init_func_types = re.search(r'static void init_func_types\(void\) {([^}]*)}', code)
55+
if not init_func_types:
56+
return {}
57+
ret = {}
58+
for entry in re.findall(r'func_types\[(\d+)\] = wasm_rt_register_func_type\((\d+), (\d+),? ?([^)]+)?\);', init_func_types[0]):
59+
index, params, results, types = entry
60+
index = int(index)
61+
params = int(params)
62+
results = int(results)
63+
types = types.split(', ')
64+
sig = ''
65+
for i in range(params):
66+
sig += c_to_s(types[i])
67+
if results == 0:
68+
sig = 'v' + sig
69+
else:
70+
assert results == 1, 'no multivalue support'
71+
sig = c_to_s(types[-1]) + sig
72+
ret[sig] = index
73+
return ret
74+
75+
1276
def do_wasm2c(infile):
1377
assert Settings.STANDALONE_WASM
1478
WASM2C = NODE_JS + [path_from_root('node_modules', 'wasm2c', 'wasm2c.js')]
@@ -72,39 +136,55 @@ def bundle_file(total, filename):
72136
# generate the necessary invokes
73137
invokes = []
74138
for sig in re.findall(r"\/\* import\: 'env' 'invoke_(\w+)' \*\/", total):
75-
def s_to_c(s):
76-
if s == 'v':
77-
return 'void'
78-
elif s == 'i':
79-
return 'u32'
80-
elif s == 'j':
81-
return 'u64'
82-
elif s == 'f':
83-
return 'f32'
84-
elif s == 'd':
85-
return 'f64'
86-
else:
87-
exit_with_error('invalid sig element:' + str(s))
139+
all_func_types = get_func_types(total)
88140

89141
def name(i):
90142
return 'a' + str(i)
91143

92144
wabt_sig = sig[0] + 'i' + sig[1:]
93-
typed_args = ['u32 fptr'] + [s_to_c(sig[i]) + ' ' + name(i) for i in range(1, len(sig))]
94-
types = ['u32'] + [s_to_c(sig[i]) for i in range(1, len(sig))]
95-
args = ['fptr'] + [name(i) for i in range(1, len(sig))]
96-
invokes.append(
97-
'%s_INVOKE_IMPL(%sZ_envZ_invoke_%sZ_%s, (%s), (%s), (%s), Z_dynCall_%sZ_%s);' % (
98-
'VOID' if sig[0] == 'v' else 'RETURNING',
99-
(s_to_c(sig[0]) + ', ') if sig[0] != 'v' else '',
100-
sig,
101-
wabt_sig,
102-
', '.join(typed_args),
103-
', '.join(types),
104-
', '.join(args),
105-
sig,
106-
wabt_sig
107-
))
145+
typed_args = [s_to_c(sig[i]) + ' ' + name(i) for i in range(1, len(sig))]
146+
full_typed_args = ['u32 fptr'] + typed_args
147+
types = [s_to_c(sig[i]) for i in range(1, len(sig))]
148+
args = [name(i) for i in range(1, len(sig))]
149+
c_func_type = s_to_c(sig[0]) + ' (*)(' + (', '.join(types) if types else 'void') + ')'
150+
if sig not in all_func_types:
151+
exit_with_error('could not find signature ' + sig + ' in function types ' + str(all_func_types))
152+
type_index = all_func_types[sig]
153+
154+
invokes.append(r'''
155+
IMPORT_IMPL(%(return_type)s, Z_envZ_invoke_%(sig)sZ_%(wabt_sig)s, (%(full_typed_args)s), {
156+
VERBOSE_LOG("invoke\n"); // waka
157+
u32 sp = Z_stackSaveZ_iv();
158+
if (next_setjmp >= MAX_SETJMP_STACK) {
159+
abort_with_message("too many nested setjmps");
160+
}
161+
u32 id = next_setjmp++;
162+
int result = setjmp(setjmp_stack[id]);
163+
%(declare_return)s
164+
if (result == 0) {
165+
%(receive)sCALL_INDIRECT(w2c___indirect_function_table, %(c_func_type)s, %(type_index)s, fptr %(args)s);
166+
/* if we got here, no longjmp or exception happened, we returned normally */
167+
} else {
168+
/* A longjmp or an exception took us here. */
169+
Z_stackRestoreZ_vi(sp);
170+
Z_setThrewZ_vii(1, 0);
171+
}
172+
next_setjmp--;
173+
%(return)s
174+
});
175+
''' % {
176+
'return_type': s_to_c(sig[0]) if sig[0] != 'v' else 'void',
177+
'sig': sig,
178+
'wabt_sig': wabt_sig,
179+
'full_typed_args': ', '.join(full_typed_args),
180+
'type_index': type_index,
181+
'c_func_type': c_func_type,
182+
'args': (', ' + ', '.join(args)) if args else '',
183+
'declare_return': (s_to_c(sig[0]) + ' returned_value = 0;') if sig[0] != 'v' else '',
184+
'receive': 'returned_value = ' if sig[0] != 'v' else '',
185+
'return': 'return returned_value;' if sig[0] != 'v' else ''
186+
})
187+
108188
total += '\n'.join(invokes)
109189
# write out the final file
110190
with open(c_file, 'w') as out:

tools/wasm2c/base.c

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -113,48 +113,6 @@ static jmp_buf setjmp_stack[MAX_SETJMP_STACK];
113113

114114
static u32 next_setjmp = 0;
115115

116-
#define VOID_INVOKE_IMPL(name, typed_args, types, args, dyncall) \
117-
IMPORT_IMPL(void, name, typed_args, { \
118-
VERBOSE_LOG("invoke " #name " " #dyncall "\n"); \
119-
u32 sp = Z_stackSaveZ_iv(); \
120-
if (next_setjmp >= MAX_SETJMP_STACK) { \
121-
abort_with_message("too many nested setjmps"); \
122-
} \
123-
u32 id = next_setjmp++; \
124-
int result = setjmp(setjmp_stack[id]); \
125-
if (result == 0) { \
126-
(* dyncall) args; \
127-
/* if we got here, no longjmp or exception happened, we returned normally */ \
128-
} else { \
129-
/* A longjmp or an exception took us here. */ \
130-
Z_stackRestoreZ_vi(sp); \
131-
Z_setThrewZ_vii(1, 0); \
132-
} \
133-
next_setjmp--; \
134-
});
135-
136-
#define RETURNING_INVOKE_IMPL(ret, name, typed_args, types, args, dyncall) \
137-
IMPORT_IMPL(ret, name, typed_args, { \
138-
VERBOSE_LOG("invoke " #name " " #dyncall "\n"); \
139-
u32 sp = Z_stackSaveZ_iv(); \
140-
if (next_setjmp >= MAX_SETJMP_STACK) { \
141-
abort_with_message("too many nested setjmps"); \
142-
} \
143-
u32 id = next_setjmp++; \
144-
int result = setjmp(setjmp_stack[id]); \
145-
ret returned_value = 0; \
146-
if (result == 0) { \
147-
returned_value = (* dyncall) args; \
148-
/* if we got here, no longjmp or exception happened, we returned normally */ \
149-
} else { \
150-
/* A longjmp or an exception took us here. */ \
151-
Z_stackRestoreZ_vi(sp); \
152-
Z_setThrewZ_vii(1, 0); \
153-
} \
154-
next_setjmp--; \
155-
return returned_value; \
156-
});
157-
158116
// Declare an export that may be needed and may not be. For example if longjmp
159117
// is included then we need setThrew, but we must declare setThrew so that
160118
// the C compiler can build us without error if longjmp is not actually used.

0 commit comments

Comments
 (0)