/src/wasm3/source/m3_parse.c
Line | Count | Source |
1 | | // |
2 | | // m3_parse.c |
3 | | // |
4 | | // Created by Steven Massey on 4/19/19. |
5 | | // Copyright © 2019 Steven Massey. All rights reserved. |
6 | | // |
7 | | |
8 | | #include "m3_env.h" |
9 | | #include "m3_compile.h" |
10 | | #include "m3_exception.h" |
11 | | #include "m3_info.h" |
12 | | |
13 | | |
14 | | M3Result ParseType_Table (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
15 | 3 | { |
16 | 3 | M3Result result = m3Err_none; |
17 | | |
18 | 3 | u32 numTables; |
19 | 3 | _ (ReadLEB_u32 (& numTables, & i_bytes, i_end)); m3log (parse, "** Table [%d]", numTables); |
20 | | |
21 | | // MVP: at most one table, counting any that was already imported |
22 | 1 | _throwif (m3Err_wasmMalformed, numTables > 1); |
23 | 1 | _throwif (m3Err_wasmMalformed, numTables and io_module->hasTable); |
24 | | |
25 | 1 | for (u32 i = 0; i < numTables; ++i) |
26 | 1 | { |
27 | 1 | u8 elemType; |
28 | 1 | _ (Read_u8 (& elemType, & i_bytes, i_end)); |
29 | | // Spec: element type must be funcref (0x70) |
30 | 0 | _throwif (m3Err_wasmMalformed, elemType != 0x70); |
31 | |
|
32 | 0 | u8 flag; |
33 | 0 | _ (ReadLEB_u7 (& flag, & i_bytes, i_end)); |
34 | 0 | u32 initSize; |
35 | 0 | _ (ReadLEB_u32 (& initSize, & i_bytes, i_end)); |
36 | 0 | if (flag & 1) { |
37 | 0 | u32 maxSize; |
38 | 0 | _ (ReadLEB_u32 (& maxSize, & i_bytes, i_end)); |
39 | 0 | _throwif (m3Err_wasmMalformed, maxSize < initSize); |
40 | 0 | } |
41 | 0 | io_module->hasTable = true; |
42 | 0 | } |
43 | | |
44 | 0 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
45 | |
|
46 | 3 | _catch: return result; |
47 | 0 | } |
48 | | |
49 | | |
50 | | M3Result ParseType_Memory (M3MemoryInfo * o_memory, bytes_t * io_bytes, cbytes_t i_end) |
51 | 71 | { |
52 | 71 | M3Result result = m3Err_none; |
53 | | |
54 | 71 | u8 flag; |
55 | | |
56 | 71 | _ (ReadLEB_u7 (& flag, io_bytes, i_end)); // really a u1 |
57 | 68 | _ (ReadLEB_u32 (& o_memory->initPages, io_bytes, i_end)); |
58 | | |
59 | 66 | o_memory->maxPages = 0; |
60 | 66 | if (flag & (1u << 0)) |
61 | 31 | { |
62 | 31 | _ (ReadLEB_u32 (& o_memory->maxPages, io_bytes, i_end)); |
63 | | |
64 | | // Spec: memory limits validation - max must not be less than init |
65 | 28 | _throwif (m3Err_wasmMalformed, o_memory->maxPages < o_memory->initPages); |
66 | 22 | } |
67 | | |
68 | 57 | o_memory->pageSize = 0; |
69 | 57 | if (flag & (1u << 3)) { |
70 | 14 | u32 logPageSize; |
71 | 14 | _ (ReadLEB_u32 (& logPageSize, io_bytes, i_end)); |
72 | 9 | o_memory->pageSize = 1u << logPageSize; |
73 | 9 | } |
74 | | |
75 | | // Spec: memory limits must be valid within range 2^16 (65536 pages) |
76 | | // Only enforce for standard page size (no custom page size flag) |
77 | 52 | if (!(flag & (1u << 3))) |
78 | 43 | { |
79 | 43 | _throwif (m3Err_wasmMalformed, o_memory->initPages > 65536); |
80 | 41 | if (flag & (1u << 0)) |
81 | 35 | _throwif (m3Err_wasmMalformed, o_memory->maxPages > 65536); |
82 | 35 | } |
83 | | |
84 | 71 | _catch: return result; |
85 | 52 | } |
86 | | |
87 | | |
88 | | M3Result ParseSection_Type (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
89 | 974 | { |
90 | 974 | IM3FuncType ftype = NULL; |
91 | | |
92 | 974 | _try { |
93 | 974 | u32 numTypes; |
94 | 974 | _ (ReadLEB_u32 (& numTypes, & i_bytes, i_end)); m3log (parse, "** Type [%d]", numTypes); |
95 | | |
96 | 973 | _throwif("too many types", numTypes > d_m3MaxSaneTypesCount); |
97 | | |
98 | 968 | if (numTypes) |
99 | 963 | { |
100 | | // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment) |
101 | 963 | io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes); |
102 | 963 | _throwifnull (io_module->funcTypes); |
103 | 963 | io_module->numFuncTypes = numTypes; |
104 | | |
105 | 2.18k | for (u32 i = 0; i < numTypes; ++i) |
106 | 1.39k | { |
107 | 1.39k | i8 form; |
108 | 1.39k | _ (ReadLEB_i7 (& form, & i_bytes, i_end)); |
109 | 1.35k | _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP |
110 | | |
111 | 1.31k | u32 numArgs; |
112 | 1.31k | _ (ReadLEB_u32 (& numArgs, & i_bytes, i_end)); |
113 | | |
114 | 1.30k | _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount); |
115 | | #if defined(M3_COMPILER_MSVC) |
116 | | u8 argTypes [d_m3MaxSaneFunctionArgRetCount]; |
117 | | #else |
118 | 1.29k | u8 argTypes[numArgs+1]; // make ubsan happy |
119 | 1.29k | #endif |
120 | 5.54k | for (u32 a = 0; a < numArgs; ++a) |
121 | 4.28k | { |
122 | 4.28k | i8 wasmType; |
123 | 4.28k | u8 argType; |
124 | 4.28k | _ (ReadLEB_i7 (& wasmType, & i_bytes, i_end)); |
125 | 4.27k | _ (NormalizeType (& argType, wasmType)); |
126 | | |
127 | 4.25k | argTypes[a] = argType; |
128 | 4.25k | } |
129 | | |
130 | 1.26k | u32 numRets; |
131 | 1.26k | _ (ReadLEB_u32 (& numRets, & i_bytes, i_end)); |
132 | 1.25k | _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount); |
133 | | |
134 | 1.24k | _ (AllocFuncType (& ftype, numRets + numArgs)); |
135 | 1.24k | ftype->numArgs = numArgs; |
136 | 1.24k | ftype->numRets = numRets; |
137 | | |
138 | 3.84k | for (u32 r = 0; r < numRets; ++r) |
139 | 2.62k | { |
140 | 2.62k | i8 wasmType; |
141 | 2.62k | u8 retType; |
142 | 2.62k | _ (ReadLEB_i7 (& wasmType, & i_bytes, i_end)); |
143 | 2.61k | _ (NormalizeType (& retType, wasmType)); |
144 | | |
145 | 2.60k | ftype->types[r] = retType; |
146 | 2.60k | } |
147 | 1.22k | memcpy (ftype->types + numRets, argTypes, numArgs); m3log (parse, " type %2d: %s", i, SPrintFuncTypeSignature (ftype)); |
148 | | |
149 | 1.22k | Environment_AddFuncType (io_module->environment, & ftype); |
150 | 1.22k | io_module->funcTypes [i] = ftype; |
151 | 1.22k | ftype = NULL; // ownership transferred to environment |
152 | 1.22k | } |
153 | 963 | } |
154 | | |
155 | 793 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
156 | | |
157 | 974 | } _catch: |
158 | | |
159 | 974 | if (result) |
160 | 244 | { |
161 | 244 | m3_Free (ftype); |
162 | | // FIX: M3FuncTypes in the table are leaked |
163 | 244 | m3_Free (io_module->funcTypes); |
164 | 244 | io_module->numFuncTypes = 0; |
165 | 244 | } |
166 | | |
167 | 974 | return result; |
168 | 730 | } |
169 | | |
170 | | |
171 | | M3Result ParseSection_Function (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
172 | 777 | { |
173 | 777 | M3Result result = m3Err_none; |
174 | | |
175 | 777 | u32 numFunctions; |
176 | 777 | _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Function [%d]", numFunctions); |
177 | | |
178 | 775 | _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount); |
179 | | |
180 | 767 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions + numFunctions)); |
181 | | |
182 | 2.20k | for (u32 i = 0; i < numFunctions; ++i) |
183 | 1.52k | { |
184 | 1.52k | u32 funcTypeIndex; |
185 | 1.52k | _ (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end)); |
186 | | |
187 | 1.48k | _ (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */)); |
188 | 1.44k | } |
189 | | |
190 | 689 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
191 | | |
192 | 777 | _catch: return result; |
193 | 688 | } |
194 | | |
195 | | |
196 | | M3Result ParseSection_Import (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
197 | 76 | { |
198 | 76 | M3Result result = m3Err_none; |
199 | | |
200 | 76 | M3ImportInfo import = { NULL, NULL }, clearImport = { NULL, NULL }; |
201 | | |
202 | 76 | u32 numImports; |
203 | 76 | _ (ReadLEB_u32 (& numImports, & i_bytes, i_end)); m3log (parse, "** Import [%d]", numImports); |
204 | | |
205 | 73 | _throwif("too many imports", numImports > d_m3MaxSaneImportsCount); |
206 | | |
207 | | // Most imports are functions, so we won't waste much space anyway (if any) |
208 | 62 | _ (Module_PreallocFunctions(io_module, numImports)); |
209 | | |
210 | 208 | for (u32 i = 0; i < numImports; ++i) |
211 | 196 | { |
212 | 196 | u8 importKind; |
213 | | |
214 | 196 | _ (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end)); |
215 | 169 | _ (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end)); |
216 | 168 | _ (Read_u8 (& importKind, & i_bytes, i_end)); m3log (parse, " kind: %d '%s.%s' ", |
217 | 167 | (u32) importKind, import.moduleUtf8, import.fieldUtf8); |
218 | 167 | switch (importKind) |
219 | 167 | { |
220 | 116 | case d_externalKind_function: |
221 | 116 | { |
222 | 116 | u32 typeIndex; |
223 | 116 | _ (ReadLEB_u32 (& typeIndex, & i_bytes, i_end)) |
224 | | |
225 | 115 | _ (Module_AddFunction (io_module, typeIndex, & import)) |
226 | 115 | import = clearImport; |
227 | | |
228 | 115 | io_module->numFuncImports++; |
229 | 115 | } |
230 | 0 | break; |
231 | | |
232 | 7 | case d_externalKind_table: |
233 | 7 | { |
234 | | // Parse and validate table type (elem type + limits) |
235 | 7 | u8 elemType; |
236 | 7 | _ (Read_u8 (& elemType, & i_bytes, i_end)); |
237 | 5 | _throwif (m3Err_wasmMalformed, elemType != 0x70); // must be funcref |
238 | 0 | u8 flag; |
239 | 0 | _ (ReadLEB_u7 (& flag, & i_bytes, i_end)); |
240 | 0 | u32 initSize; |
241 | 0 | _ (ReadLEB_u32 (& initSize, & i_bytes, i_end)); |
242 | 0 | if (flag & 1) { |
243 | 0 | u32 maxSize; |
244 | 0 | _ (ReadLEB_u32 (& maxSize, & i_bytes, i_end)); |
245 | 0 | } |
246 | 0 | io_module->hasTable = true; |
247 | 0 | } |
248 | 0 | break; |
249 | | |
250 | 33 | case d_externalKind_memory: |
251 | 33 | { |
252 | 33 | _ (ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end)); |
253 | 30 | io_module->memoryImported = true; |
254 | 30 | io_module->memoryImport = import; |
255 | 30 | import = clearImport; |
256 | 30 | } |
257 | 0 | break; |
258 | | |
259 | 10 | case d_externalKind_global: |
260 | 10 | { |
261 | 10 | i8 waType; |
262 | 10 | u8 type, isMutable; |
263 | | |
264 | 10 | _ (ReadLEB_i7 (& waType, & i_bytes, i_end)); |
265 | 9 | _ (NormalizeType (& type, waType)); |
266 | 7 | _ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: %s mutable=%d", c_waTypes [type], (u32) isMutable); |
267 | 6 | _throwif (m3Err_wasmMalformed, isMutable > 1); |
268 | | |
269 | 1 | IM3Global global; |
270 | 1 | _ (Module_AddGlobal (io_module, & global, type, isMutable, true /* isImport */)); |
271 | 1 | global->import = import; |
272 | 1 | import = clearImport; |
273 | 1 | } |
274 | 0 | break; |
275 | | |
276 | 1 | default: |
277 | 1 | _throw (m3Err_wasmMalformed); |
278 | 167 | } |
279 | | |
280 | 146 | FreeImportInfo (& import); |
281 | 146 | } |
282 | | |
283 | 12 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
284 | | |
285 | 76 | _catch: |
286 | | |
287 | 76 | FreeImportInfo (& import); |
288 | | |
289 | 76 | return result; |
290 | 10 | } |
291 | | |
292 | | |
293 | | M3Result ParseSection_Export (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
294 | 131 | { |
295 | 131 | M3Result result = m3Err_none; |
296 | 131 | const char * utf8 = NULL; |
297 | 131 | #if d_m3EnableValidation |
298 | | // We store name pointers + lengths to handle embedded NUL bytes correctly |
299 | 131 | typedef struct { const u8 * ptr; u16 len; } ExportName; |
300 | 131 | ExportName * exportNames = NULL; |
301 | 131 | #endif |
302 | | |
303 | 131 | u32 numExports; |
304 | 131 | _ (ReadLEB_u32 (& numExports, & i_bytes, i_end)); m3log (parse, "** Export [%d]", numExports); |
305 | | |
306 | 129 | _throwif("too many exports", numExports > d_m3MaxSaneExportsCount); |
307 | | |
308 | 118 | #if d_m3EnableValidation |
309 | | // Spec: all export names must be different |
310 | 118 | if (numExports > 1) |
311 | 101 | { |
312 | 101 | exportNames = (ExportName *) m3_Malloc ("exportNames", sizeof(ExportName) * numExports); |
313 | 101 | } |
314 | 118 | #endif |
315 | | |
316 | 140 | for (u32 i = 0; i < numExports; ++i) |
317 | 136 | { |
318 | 136 | u8 exportKind; |
319 | 136 | u32 index; |
320 | | |
321 | | // Read name length and remember raw position for uniqueness check |
322 | 136 | #if d_m3EnableValidation |
323 | 136 | const u8 * nameStart = i_bytes; |
324 | 136 | u32 nameLen = 0; |
325 | 136 | { |
326 | 136 | bytes_t tmp = i_bytes; |
327 | 136 | M3Result rl = ReadLEB_u32 (& nameLen, & tmp, i_end); |
328 | 136 | if (rl) { m3_Free(exportNames); _throw(rl); } |
329 | 116 | nameStart = tmp; // points to the raw name bytes |
330 | 116 | } |
331 | 0 | #endif |
332 | | |
333 | 116 | _ (Read_utf8 (& utf8, & i_bytes, i_end)); |
334 | 99 | _ (Read_u8 (& exportKind, & i_bytes, i_end)); |
335 | 94 | _ (ReadLEB_u32 (& index, & i_bytes, i_end)); m3log (parse, " index: %3d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8); |
336 | | |
337 | 93 | #if d_m3EnableValidation |
338 | 93 | if (exportNames) |
339 | 81 | { |
340 | 87 | for (u32 j = 0; j < i; ++j) |
341 | 12 | { |
342 | 12 | if (exportNames[j].len == nameLen && |
343 | 7 | memcmp (exportNames[j].ptr, nameStart, nameLen) == 0) |
344 | 6 | { |
345 | 6 | m3_Free (exportNames); |
346 | 6 | _throw (m3Err_wasmMalformed); // duplicate export name |
347 | 0 | } |
348 | 12 | } |
349 | 75 | exportNames[i].ptr = nameStart; |
350 | 75 | exportNames[i].len = (u16)nameLen; |
351 | 75 | } |
352 | 87 | #endif |
353 | | |
354 | 87 | if (exportKind == d_externalKind_function) |
355 | 34 | { |
356 | 34 | _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions); |
357 | 5 | IM3Function func = &(io_module->functions [index]); |
358 | 5 | if (func->numNames < d_m3MaxDuplicateFunctionImpl) |
359 | 5 | { |
360 | 5 | func->names[func->numNames++] = utf8; |
361 | 5 | func->export_name = utf8; |
362 | 5 | utf8 = NULL; // ownership transferred to M3Function |
363 | 5 | } |
364 | 5 | } |
365 | 53 | else if (exportKind == d_externalKind_global) |
366 | 29 | { |
367 | 29 | _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals); |
368 | 0 | IM3Global global = &(io_module->globals [index]); |
369 | 0 | m3_Free (global->name); |
370 | 0 | global->name = utf8; |
371 | 0 | utf8 = NULL; // ownership transferred to M3Global |
372 | 0 | } |
373 | 24 | else if (exportKind == d_externalKind_memory) |
374 | 4 | { |
375 | 4 | _throwif(m3Err_wasmMalformed, index != 0); |
376 | 1 | _throwif(m3Err_wasmMalformed, not (io_module->memoryImported or io_module->memoryDeclared)); |
377 | 0 | m3_Free (io_module->memoryExportName); |
378 | 0 | io_module->memoryExportName = utf8; |
379 | 0 | utf8 = NULL; // ownership transferred to M3Module |
380 | 0 | } |
381 | 20 | else if (exportKind == d_externalKind_table) |
382 | 3 | { |
383 | 3 | _throwif(m3Err_wasmMalformed, index != 0); |
384 | 1 | _throwif(m3Err_wasmMalformed, not io_module->hasTable); |
385 | 0 | m3_Free (io_module->table0ExportName); |
386 | 0 | io_module->table0ExportName = utf8; |
387 | 0 | utf8 = NULL; // ownership transferred to M3Module |
388 | 0 | } |
389 | | |
390 | 22 | m3_Free (utf8); |
391 | 22 | } |
392 | | |
393 | 4 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
394 | | |
395 | 131 | _catch: |
396 | 131 | m3_Free (utf8); |
397 | 131 | #if d_m3EnableValidation |
398 | 131 | m3_Free (exportNames); |
399 | 131 | #endif |
400 | 131 | return result; |
401 | 4 | } |
402 | | |
403 | | |
404 | | M3Result ParseSection_Start (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
405 | 20 | { |
406 | 20 | M3Result result = m3Err_none; |
407 | | |
408 | 20 | u32 startFuncIndex; |
409 | 20 | _ (ReadLEB_u32 (& startFuncIndex, & i_bytes, i_end)); m3log (parse, "** Start Function: %d", startFuncIndex); |
410 | | |
411 | 18 | if (startFuncIndex < io_module->numFunctions) |
412 | 1 | { |
413 | | // Spec: start function type must be [] -> [] |
414 | 1 | IM3Function func = & io_module->functions [startFuncIndex]; |
415 | 1 | if (func->funcType) |
416 | 1 | { |
417 | 1 | _throwif (m3Err_wasmMalformed, |
418 | 1 | func->funcType->numArgs != 0 || func->funcType->numRets != 0); |
419 | 1 | } |
420 | | |
421 | 1 | io_module->startFunction = startFuncIndex; |
422 | 1 | } |
423 | 17 | else result = "start function index out of bounds"; |
424 | | |
425 | 18 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
426 | | |
427 | 20 | _catch: return result; |
428 | 16 | } |
429 | | |
430 | | |
431 | | M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end) |
432 | 4 | { |
433 | 4 | M3Result result = m3Err_none; |
434 | | |
435 | | // this doesn't generate code pages. just walks the wasm bytecode to find the end |
436 | | |
437 | | #if defined(d_m3PreferStaticAlloc) |
438 | | static M3Compilation compilation; |
439 | | #else |
440 | 4 | M3Compilation compilation; |
441 | 4 | #endif |
442 | 4 | compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end, .isInitExpr = true }; |
443 | | |
444 | 4 | result = CompileBlockStatements (& compilation); |
445 | | |
446 | 4 | * io_bytes = compilation.wasm; |
447 | | |
448 | 4 | return result; |
449 | 4 | } |
450 | | |
451 | | |
452 | | M3Result ParseSection_Element (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
453 | 59 | { |
454 | 59 | M3Result result = m3Err_none; |
455 | | |
456 | 59 | u32 numSegments; |
457 | 59 | bytes_t pos; |
458 | 59 | _ (ReadLEB_u32 (& numSegments, & i_bytes, i_end)); m3log (parse, "** Element [%d]", numSegments); |
459 | | |
460 | 57 | _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments); |
461 | | |
462 | | // Element segments need a table to populate |
463 | 51 | _throwif (m3Err_wasmMalformed, numSegments and not io_module->hasTable); |
464 | | |
465 | 2 | io_module->elementSection = i_bytes; |
466 | 2 | io_module->elementSectionEnd = i_end; |
467 | 2 | io_module->numElementSegments = numSegments; |
468 | | |
469 | | // Walk the section to validate structure and detect section size mismatch. |
470 | | // The actual element initialization happens later in InitElements. |
471 | 2 | pos = i_bytes; |
472 | 2 | for (u32 i = 0; i < numSegments; ++i) |
473 | 0 | { |
474 | 0 | u32 tableIndex; |
475 | 0 | _ (ReadLEB_u32 (& tableIndex, & pos, i_end)); |
476 | | |
477 | | // Walk the init expression (offset) to find its end |
478 | 0 | _ (Parse_InitExpr (io_module, & pos, i_end)); |
479 | |
|
480 | 0 | u32 numElements; |
481 | 0 | _ (ReadLEB_u32 (& numElements, & pos, i_end)); |
482 | |
|
483 | 0 | for (u32 e = 0; e < numElements; ++e) |
484 | 0 | { |
485 | 0 | u32 funcIndex; |
486 | 0 | _ (ReadLEB_u32 (& funcIndex, & pos, i_end)); |
487 | 0 | } |
488 | 0 | } |
489 | | |
490 | 2 | _throwif (m3Err_wasmMalformed, pos != i_end); // section size mismatch |
491 | | |
492 | 59 | _catch: return result; |
493 | 1 | } |
494 | | |
495 | | |
496 | | M3Result ParseSection_Code (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
497 | 682 | { |
498 | 682 | M3Result result; |
499 | | |
500 | 682 | u32 numFunctions; |
501 | 682 | _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Code [%d]", numFunctions); |
502 | | |
503 | 679 | if (numFunctions != io_module->numFunctions - io_module->numFuncImports) |
504 | 26 | { |
505 | 26 | _throw ("mismatched function count in code section"); |
506 | 0 | } |
507 | | |
508 | 1.36k | for (u32 f = 0; f < numFunctions; ++f) |
509 | 751 | { |
510 | 751 | const u8 * start = i_bytes; |
511 | | |
512 | 751 | u32 size; |
513 | 751 | _ (ReadLEB_u32 (& size, & i_bytes, i_end)); |
514 | | |
515 | 746 | if (size) |
516 | 658 | { |
517 | 658 | const u8 * ptr = i_bytes; |
518 | 658 | i_bytes += size; |
519 | | |
520 | 658 | if (i_bytes <= i_end) |
521 | 627 | { |
522 | | /* |
523 | | u32 numLocalBlocks; |
524 | | _ (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end)); m3log (parse, " code size: %-4d", size); |
525 | | |
526 | | u32 numLocals = 0; |
527 | | |
528 | | for (u32 l = 0; l < numLocalBlocks; ++l) |
529 | | { |
530 | | u32 varCount; |
531 | | i8 wasmType; |
532 | | u8 normalType; |
533 | | |
534 | | _ (ReadLEB_u32 (& varCount, & ptr, i_end)); |
535 | | _ (ReadLEB_i7 (& wasmType, & ptr, i_end)); |
536 | | _ (NormalizeType (& normalType, wasmType)); |
537 | | |
538 | | numLocals += varCount; m3log (parse, " %2d locals; type: '%s'", varCount, c_waTypes [normalType]); |
539 | | } |
540 | | */ |
541 | | |
542 | 627 | IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports); |
543 | | |
544 | 627 | func->module = io_module; |
545 | 627 | func->wasm = start; |
546 | 627 | func->wasmEnd = i_bytes; |
547 | | //func->ownsWasmCode = io_module->hasWasmCodeCopy; |
548 | | // func->numLocals = numLocals; |
549 | 627 | } |
550 | 627 | else _throw (m3Err_wasmSectionOverrun); |
551 | 627 | } |
552 | 746 | } |
553 | | |
554 | 682 | _catch: |
555 | | |
556 | 682 | if (not result and i_bytes != i_end) |
557 | 3 | result = m3Err_wasmSectionUnderrun; |
558 | | |
559 | 682 | return result; |
560 | 653 | } |
561 | | |
562 | | |
563 | | M3Result ParseSection_Data (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
564 | 78 | { |
565 | 78 | M3Result result = m3Err_none; |
566 | | |
567 | 78 | u32 numDataSegments; |
568 | 78 | _ (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end)); m3log (parse, "** Data [%d]", numDataSegments); |
569 | | |
570 | 75 | _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments); |
571 | | |
572 | 63 | io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments); |
573 | 63 | _throwifnull(io_module->dataSegments); |
574 | 63 | io_module->numDataSegments = numDataSegments; |
575 | | |
576 | 63 | for (u32 i = 0; i < numDataSegments; ++i) |
577 | 61 | { |
578 | 61 | M3DataSegment * segment = & io_module->dataSegments [i]; |
579 | | |
580 | 61 | _ (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end)); |
581 | | |
582 | | // Spec: MVP only supports memory index 0, and it has to exist |
583 | 35 | _throwif (m3Err_wasmMalformed, segment->memoryRegion != 0); |
584 | 1 | _throwif (m3Err_wasmMalformed, not (io_module->memoryImported or io_module->memoryDeclared)); |
585 | |
|
586 | 0 | segment->initExpr = i_bytes; |
587 | 0 | _ (Parse_InitExpr (io_module, & i_bytes, i_end)); |
588 | 0 | segment->initExprSize = (u32) (i_bytes - segment->initExpr); |
589 | |
|
590 | 0 | _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1); |
591 | |
|
592 | 0 | _ (ReadLEB_u32 (& segment->size, & i_bytes, i_end)); |
593 | 0 | segment->data = i_bytes; m3log (parse, " segment [%u] memory: %u; expr-size: %d; size: %d", |
594 | 0 | i, segment->memoryRegion, segment->initExprSize, segment->size); |
595 | 0 | i_bytes += segment->size; |
596 | |
|
597 | 0 | _throwif("data segment underflow", i_bytes > i_end); |
598 | 0 | } |
599 | | |
600 | 2 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
601 | | |
602 | 78 | _catch: |
603 | | |
604 | 78 | return result; |
605 | 2 | } |
606 | | |
607 | | |
608 | | M3Result ParseSection_Memory (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
609 | 60 | { |
610 | 60 | M3Result result = m3Err_none; |
611 | | |
612 | | // TODO: MVP; assert no memory imported |
613 | | |
614 | 60 | u32 numMemories; |
615 | 60 | _ (ReadLEB_u32 (& numMemories, & i_bytes, i_end)); m3log (parse, "** Memory [%d]", numMemories); |
616 | | |
617 | 58 | _throwif (m3Err_tooManyMemorySections, numMemories > 1); |
618 | | |
619 | 41 | if (numMemories) |
620 | 38 | { |
621 | 38 | _ (ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end)); |
622 | 14 | io_module->memoryDeclared = true; |
623 | 14 | } |
624 | | |
625 | 17 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
626 | | |
627 | 60 | _catch: return result; |
628 | 16 | } |
629 | | |
630 | | |
631 | | M3Result ParseSection_Global (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
632 | 73 | { |
633 | 73 | M3Result result = m3Err_none; |
634 | | |
635 | 73 | u32 numGlobals; |
636 | 73 | _ (ReadLEB_u32 (& numGlobals, & i_bytes, i_end)); m3log (parse, "** Global [%d]", numGlobals); |
637 | | |
638 | 72 | _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount); |
639 | | |
640 | 62 | for (u32 i = 0; i < numGlobals; ++i) |
641 | 59 | { |
642 | 59 | i8 waType; |
643 | 59 | u8 type, isMutable; |
644 | | |
645 | 59 | _ (ReadLEB_i7 (& waType, & i_bytes, i_end)); |
646 | 38 | _ (NormalizeType (& type, waType)); |
647 | 32 | _ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: [%d] %s mutable: %d", i, c_waTypes [type], (u32) isMutable); |
648 | 29 | _throwif (m3Err_wasmMalformed, isMutable > 1); |
649 | | |
650 | 4 | IM3Global global; |
651 | 4 | _ (Module_AddGlobal (io_module, & global, type, isMutable, false /* isImport */)); |
652 | | |
653 | 4 | global->initExpr = i_bytes; |
654 | 4 | _ (Parse_InitExpr (io_module, & i_bytes, i_end)); |
655 | 1 | global->initExprSize = (u32) (i_bytes - global->initExpr); |
656 | | |
657 | 1 | _throwif (m3Err_wasmMissingInitExpr, global->initExprSize <= 1); |
658 | 0 | } |
659 | | |
660 | 3 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
661 | | |
662 | 73 | _catch: return result; |
663 | 2 | } |
664 | | |
665 | | |
666 | | M3Result ParseSection_Name (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
667 | 1.18k | { |
668 | 1.18k | M3Result result = m3Err_none; |
669 | | |
670 | 1.18k | cstr_t name; |
671 | | |
672 | 3.27k | while (i_bytes < i_end) |
673 | 2.16k | { |
674 | 2.16k | u8 nameType; |
675 | 2.16k | u32 payloadLength; |
676 | | |
677 | 2.16k | _ (ReadLEB_u7 (& nameType, & i_bytes, i_end)); |
678 | 2.16k | _ (ReadLEB_u32 (& payloadLength, & i_bytes, i_end)); |
679 | | |
680 | 2.15k | bytes_t start = i_bytes; |
681 | 2.15k | if (nameType == 1) |
682 | 1.28k | { |
683 | 1.28k | u32 numNames; |
684 | 1.28k | _ (ReadLEB_u32 (& numNames, & i_bytes, i_end)); |
685 | | |
686 | 1.28k | _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount); |
687 | | |
688 | 3.26k | for (u32 i = 0; i < numNames; ++i) |
689 | 2.04k | { |
690 | 2.04k | u32 index; |
691 | 2.04k | _ (ReadLEB_u32 (& index, & i_bytes, i_end)); |
692 | 2.02k | _ (Read_utf8 (& name, & i_bytes, i_end)); |
693 | | |
694 | 1.98k | if (index < io_module->numFunctions) |
695 | 1.03k | { |
696 | 1.03k | IM3Function func = &(io_module->functions [index]); |
697 | 1.03k | if (func->numNames == 0) |
698 | 615 | { |
699 | 615 | func->names[0] = name; m3log (parse, " naming function%5d: %s", index, name); |
700 | 615 | func->numNames = 1; |
701 | 615 | name = NULL; // transfer ownership |
702 | 615 | } |
703 | | // else m3log (parse, "prenamed: %s", io_module->functions [index].name); |
704 | 1.03k | } |
705 | | |
706 | 1.98k | m3_Free (name); |
707 | 1.98k | } |
708 | 1.28k | } |
709 | | |
710 | 2.08k | i_bytes = start + payloadLength; |
711 | 2.08k | } |
712 | | |
713 | 1.18k | _catch: return result; |
714 | 1.18k | } |
715 | | |
716 | | |
717 | | M3Result ParseSection_Custom (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
718 | 2.22k | { |
719 | 2.22k | M3Result result; |
720 | | |
721 | 2.22k | cstr_t name; |
722 | 2.22k | _ (Read_utf8 (& name, & i_bytes, i_end)); |
723 | 2.14k | m3log (parse, "** Custom: '%s'", name); |
724 | 2.14k | if (strcmp (name, "name") == 0) { |
725 | 1.18k | _ (ParseSection_Name(io_module, i_bytes, i_end)); |
726 | 1.11k | } else if (io_module->environment->customSectionHandler) { |
727 | 0 | _ (io_module->environment->customSectionHandler(io_module, name, i_bytes, i_end)); |
728 | 0 | } |
729 | | |
730 | 2.07k | m3_Free (name); |
731 | | |
732 | 2.22k | _catch: return result; |
733 | 2.07k | } |
734 | | |
735 | | |
736 | | M3Result ParseModuleSection (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes) |
737 | 5.16k | { |
738 | 5.16k | M3Result result = m3Err_none; |
739 | | |
740 | 5.16k | typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t); |
741 | | |
742 | 5.16k | static M3Parser s_parsers [] = |
743 | 5.16k | { |
744 | 5.16k | ParseSection_Custom, // 0 |
745 | 5.16k | ParseSection_Type, // 1 |
746 | 5.16k | ParseSection_Import, // 2 |
747 | 5.16k | ParseSection_Function, // 3 |
748 | 5.16k | ParseType_Table, // 4 |
749 | 5.16k | ParseSection_Memory, // 5 |
750 | 5.16k | ParseSection_Global, // 6 |
751 | 5.16k | ParseSection_Export, // 7 |
752 | 5.16k | ParseSection_Start, // 8 |
753 | 5.16k | ParseSection_Element, // 9 |
754 | 5.16k | ParseSection_Code, // 10 |
755 | 5.16k | ParseSection_Data, // 11 |
756 | 5.16k | NULL, // 12: TODO DataCount |
757 | 5.16k | }; |
758 | | |
759 | 5.16k | M3Parser parser = NULL; |
760 | | |
761 | 5.16k | if (i_sectionType <= 12) |
762 | 5.16k | parser = s_parsers [i_sectionType]; |
763 | | |
764 | 5.16k | if (parser) |
765 | 5.15k | { |
766 | 5.15k | cbytes_t end = i_bytes + i_numBytes; |
767 | 5.15k | result = parser (o_module, i_bytes, end); |
768 | 5.15k | } |
769 | 1 | else |
770 | 1 | { |
771 | 1 | m3log (parse, " skipped section type: %d", (u32) i_sectionType); |
772 | 1 | } |
773 | | |
774 | 5.16k | return result; |
775 | 5.16k | } |
776 | | |
777 | | |
778 | | M3Result m3_ParseModule (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes) |
779 | 1.91k | { |
780 | 1.91k | IM3Module module; m3log (parse, "load module: %d bytes", i_numBytes); |
781 | 1.91k | _try { |
782 | 1.91k | module = m3_AllocStruct (M3Module); |
783 | 1.91k | _throwifnull (module); |
784 | 1.91k | module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes); |
785 | 1.91k | module->startFunction = -1; |
786 | | //module->hasWasmCodeCopy = false; |
787 | 1.91k | module->environment = i_environment; |
788 | | |
789 | 1.91k | const u8 * pos = i_bytes; |
790 | 1.91k | const u8 * end = pos + i_numBytes; |
791 | | |
792 | 1.91k | module->wasmStart = pos; |
793 | 1.91k | module->wasmEnd = end; |
794 | | |
795 | 1.91k | u32 magic, version; |
796 | 1.91k | _ (Read_u32 (& magic, & pos, end)); |
797 | 1.91k | _ (Read_u32 (& version, & pos, end)); |
798 | | |
799 | 1.91k | _throwif (m3Err_wasmMalformed, magic != 0x6d736100); |
800 | 1.86k | _throwif (m3Err_incompatibleWasmVersion, version != 1); |
801 | | |
802 | 1.81k | static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder |
803 | 1.81k | u8 expectedSection = 0; |
804 | | |
805 | 5.95k | while (pos < end) |
806 | 5.26k | { |
807 | 5.26k | u8 section; |
808 | 5.26k | _ (ReadLEB_u7 (& section, & pos, end)); |
809 | | |
810 | 5.25k | if (section != 0) { |
811 | | // Ensure sections appear only once and in order |
812 | 11.8k | while (sectionsOrder[expectedSection++] != section) { |
813 | 8.87k | _throwif(m3Err_misorderedWasmSection, expectedSection >= 12); |
814 | 8.85k | } |
815 | 2.99k | } |
816 | | |
817 | 5.23k | u32 sectionLength; |
818 | 5.23k | _ (ReadLEB_u32 (& sectionLength, & pos, end)); |
819 | 5.18k | _throwif(m3Err_wasmMalformed, pos + sectionLength > end); |
820 | | |
821 | 5.16k | _ (ParseModuleSection (module, section, pos, sectionLength)); |
822 | | |
823 | 4.13k | pos += sectionLength; |
824 | 4.13k | } |
825 | | |
826 | | // Spec: if a function section exists, a code section must also exist with |
827 | | // matching count (and vice versa). ParseSection_Code checks the other |
828 | | // direction; this covers the case where the code section is missing entirely. |
829 | 690 | if (module->numFunctions > module->numFuncImports) |
830 | 613 | { |
831 | 613 | IM3Function firstNonImport = & module->functions [module->numFuncImports]; |
832 | 613 | _throwif (m3Err_wasmMalformed, firstNonImport->wasm == NULL); |
833 | 585 | } |
834 | | |
835 | 1.91k | } _catch: |
836 | | |
837 | 1.91k | if (result) |
838 | 1.25k | { |
839 | 1.25k | m3_FreeModule (module); |
840 | 1.25k | module = NULL; |
841 | 1.25k | } |
842 | | |
843 | 1.91k | * o_module = module; |
844 | | |
845 | 1.91k | return result; |
846 | 690 | } |