/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 | | // elem type + limits, shared by the table section and by table imports. |
15 | | // The declared minimum is the table's initial size; table.grow can raise it up |
16 | | // to the maximum, so an element segment reaching past the current size is out |
17 | | // of bounds. |
18 | | #if d_m3HasTypedRefs |
19 | | |
20 | | // Reads a heap type: 'func', 'extern', or the index of a function type the |
21 | | // module has already defined. Yields the heap-type bits of an m3type_t, which |
22 | | // carry the canonical index rather than the module's own, so that structurally |
23 | | // equal types compare equal across modules. |
24 | | M3Result ParseHeapType (IM3Module i_module, m3type_t * o_heapBits, bytes_t * io_bytes, cbytes_t i_end) |
25 | | { |
26 | | M3Result result = m3Err_none; |
27 | | |
28 | | i64 heap; |
29 | | _ (ReadLebSigned (& heap, 33, io_bytes, i_end)); |
30 | | |
31 | | if (heap == -d_waType_funcref) |
32 | | * o_heapBits = d_m3Type_heapAbstract; |
33 | | else if (heap == -d_waType_externref) |
34 | | * o_heapBits = d_m3Type_refExtern | d_m3Type_heapAbstract; |
35 | | else if (heap >= 0) |
36 | | { |
37 | | _throwif (m3Err_wasmMalformed, not i_module or (u64) heap >= i_module->numFuncTypes); |
38 | | |
39 | | IM3FuncType ftype = i_module->funcTypes [heap]; |
40 | | |
41 | | // null while the type section is still being read: a type may only name |
42 | | // one that precedes it, recursion belongs to a later proposal |
43 | | _throwif (m3Err_wasmMalformed, not ftype); |
44 | | |
45 | | * o_heapBits = ftype->canonicalIndex; |
46 | | } |
47 | | else _throw (m3Err_invalidTypeId); |
48 | | |
49 | | _catch: return result; |
50 | | } |
51 | | |
52 | | #endif // d_m3HasTypedRefs |
53 | | |
54 | | |
55 | | // Reads a value type, including the reference types the function references |
56 | | // proposal spells out in full: (ref ht) and (ref null ht). |
57 | | M3Result ParseValueType (IM3Module i_module, m3type_t * o_type, bytes_t * io_bytes, cbytes_t i_end) |
58 | 18.3k | { |
59 | 18.3k | M3Result result = m3Err_none; |
60 | | |
61 | 18.3k | _throwif (m3Err_wasmUnderrun, * io_bytes >= i_end); |
62 | | |
63 | | #if d_m3HasTypedRefs |
64 | | if (** io_bytes == d_waEncode_ref or ** io_bytes == d_waEncode_refNull) |
65 | | { |
66 | | u8 lead = * (* io_bytes)++; |
67 | | |
68 | | m3type_t heapBits = d_m3Type_heapAbstract; |
69 | | _ (ParseHeapType (i_module, & heapBits, io_bytes, i_end)); |
70 | | |
71 | | * o_type = d_m3Type_ref | heapBits | ((lead == d_waEncode_ref) ? d_m3Type_refNonNull : 0); |
72 | | |
73 | | return result; |
74 | | } |
75 | | #endif |
76 | | |
77 | 18.3k | i8 wasmType; |
78 | 18.3k | u8 plainType; |
79 | 18.3k | _ (ReadLEB_i7 (& wasmType, io_bytes, i_end)); |
80 | 18.3k | _ (NormalizeType (& plainType, wasmType)); |
81 | | |
82 | 18.3k | * o_type = plainType; |
83 | | |
84 | 18.3k | _catch: return result; |
85 | 18.3k | } |
86 | | |
87 | | |
88 | | // Reads an elem type and limits without registering anything: an import |
89 | | // descriptor holds on to the fields and only adds the table once it has a name. |
90 | | static |
91 | | M3Result ReadType_TableType (IM3Module i_module, M3TableInfo * o_info, bytes_t * io_bytes, cbytes_t i_end) |
92 | 357 | { |
93 | 357 | M3Result result = m3Err_none; |
94 | | |
95 | 357 | m3type_t elemType = c_m3Type_none; |
96 | 357 | u8 flag = 0; |
97 | 357 | u64 initSize = 0, maxSize = 0; |
98 | | |
99 | | // the width of the limits, and of every index into the table |
100 | 357 | u32 addrBits = 32; |
101 | | |
102 | 357 | _ (ParseValueType (i_module, & elemType, io_bytes, i_end)); |
103 | 355 | #if d_m3HasRefTypes |
104 | 355 | _throwif (m3Err_wasmMalformed, not IsRefType (BaseTypeOf(elemType))); |
105 | | #else |
106 | | _throwif (m3Err_wasmMalformed, elemType != c_m3Type_funcref); |
107 | | #endif |
108 | | |
109 | 355 | _ (ReadLEB_u7 (& flag, io_bytes, i_end)); |
110 | | |
111 | | // bit 0: has max, bit 2: table64 - the same flag memory64 gives a memory |
112 | 354 | #if d_m3HasMemory64 |
113 | 354 | _throwif (m3Err_wasmMalformed, flag & ~0x05u); |
114 | | #else |
115 | | _throwif (m3Err_wasmMalformed, flag & ~0x01u); |
116 | | #endif |
117 | | |
118 | 354 | if (flag & 0x04u) |
119 | 65 | addrBits = 64; |
120 | | |
121 | 354 | _ (ReadLebUnsigned (& initSize, addrBits, io_bytes, i_end)); |
122 | 354 | _throwif ("table overflow", initSize > d_m3MaxSaneTableSize); |
123 | | |
124 | 353 | if (flag & 0x01u) |
125 | 23 | { |
126 | 23 | _ (ReadLebUnsigned (& maxSize, addrBits, io_bytes, i_end)); |
127 | 23 | _throwif (m3Err_wasmMalformed, maxSize < initSize); |
128 | 23 | _throwif ("table overflow", maxSize > d_m3MaxSaneTableSize); |
129 | 21 | } |
130 | | |
131 | 357 | _catch: |
132 | | |
133 | 357 | o_info->elemType = elemType; |
134 | 357 | o_info->initSize = (u32) initSize; |
135 | 357 | o_info->maxSize = (u32) maxSize; |
136 | 357 | o_info->hasMax = (flag & 0x01u) != 0; |
137 | 357 | o_info->isTable64 = (flag & 0x04u) != 0; |
138 | | |
139 | 357 | return result; |
140 | 353 | } |
141 | | |
142 | | |
143 | | static |
144 | | M3Result ParseType_TableType (IM3Module io_module, bytes_t * io_bytes, cbytes_t i_end) |
145 | 321 | { |
146 | 321 | M3Result result = m3Err_none; |
147 | | |
148 | 321 | M3TableInfo info; |
149 | | |
150 | 321 | _ (ReadType_TableType (io_module, & info, io_bytes, i_end)); |
151 | 317 | _ (Module_AddTable (io_module, NULL, & info, false /* isImport */)); |
152 | | |
153 | 321 | _catch: return result; |
154 | 317 | } |
155 | | |
156 | | |
157 | | |
158 | | M3Result Parse_InitExprTyped (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end, m3type_t * o_type); |
159 | | |
160 | | M3Result ParseType_Table (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
161 | 266 | { |
162 | 266 | M3Result result = m3Err_none; |
163 | | |
164 | 266 | u32 numTables; |
165 | 266 | _ (ReadLEB_u32 (& numTables, & i_bytes, i_end)); m3log (parse, "** Table [%d]", numTables); |
166 | | |
167 | 266 | _throwif ("too many tables", (u64) numTables + io_module->numTables > d_m3MaxSaneTableCount); |
168 | | |
169 | 583 | for (u32 i = 0; i < numTables; ++i) |
170 | 321 | { |
171 | | #if d_m3HasTypedRefs |
172 | | // function-references: 0x40 0x00 introduces a table with an explicit |
173 | | // initializer. A reference type never starts with 0x40, so the two |
174 | | // forms are told apart by the first byte; the second is reserved. |
175 | | bool hasInitExpr = (i_bytes < i_end) and (* i_bytes == 0x40); |
176 | | u32 tableIndex = io_module->numTables; |
177 | | |
178 | | if (hasInitExpr) |
179 | | { |
180 | | u8 reserved; |
181 | | ++i_bytes; |
182 | | _ (Read_u8 (& reserved, & i_bytes, i_end)); |
183 | | _throwif (m3Err_wasmMalformed, reserved != 0x00); |
184 | | } |
185 | | |
186 | | _ (ParseType_TableType (io_module, & i_bytes, i_end)); |
187 | | |
188 | | if (hasInitExpr) |
189 | | { |
190 | | IM3Table table = io_module->tables [tableIndex]; |
191 | | |
192 | | m3type_t initType; |
193 | | |
194 | | table->initExpr = i_bytes; |
195 | | _ (Parse_InitExprTyped (io_module, & i_bytes, i_end, & initType)); |
196 | | table->initExprSize = (u32) (i_bytes - table->initExpr); |
197 | | _throwif (m3Err_wasmMissingInitExpr, table->initExprSize <= 1); |
198 | | |
199 | | _throwif (m3Err_typeMismatch, not IsSubTypeOf (initType, table->type)); |
200 | | } |
201 | | else |
202 | | { |
203 | | // no initializer means null, which only a nullable type can hold |
204 | | _throwif ("table of non-nullable type requires an initializer", |
205 | | not IsNullableRef (io_module->tables [tableIndex]->type)); |
206 | | } |
207 | | #else |
208 | 321 | _ (ParseType_TableType (io_module, & i_bytes, i_end)); |
209 | 317 | #endif |
210 | 317 | } |
211 | | |
212 | 262 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
213 | | |
214 | 266 | _catch: return result; |
215 | 262 | } |
216 | | |
217 | | |
218 | | M3Result ParseType_Memory (M3MemoryInfo * o_memory, bytes_t * io_bytes, cbytes_t i_end) |
219 | 3.61k | { |
220 | 3.61k | M3Result result = m3Err_none; |
221 | | |
222 | 3.61k | u8 flag; |
223 | | |
224 | | // The custom page sizes proposal admits only 1 and 65536, but nothing in |
225 | | // the engine cares which power of two it is, so any of them is accepted. |
226 | | // Declared up here so the throws below don't jump over its initialization. |
227 | 3.61k | u32 logPageSize = 16; |
228 | | |
229 | | // The width of the limits, and of everything that addresses the memory: |
230 | | // 32 unless bit 2 says memory64. |
231 | 3.61k | u32 addrBits = 32; |
232 | | |
233 | 3.61k | _ (ReadLEB_u7 (& flag, io_bytes, i_end)); |
234 | | |
235 | | // bit 0: has max, bit 2: memory64, bit 3: custom page size. bit 1 (shared) |
236 | | // belongs to a proposal we don't implement. |
237 | 3.60k | #if d_m3HasMemory64 |
238 | 3.60k | _throwif (m3Err_wasmMalformed, flag & ~0x0Du); |
239 | | #else |
240 | | _throwif (m3Err_wasmMalformed, flag & ~0x09u); |
241 | | #endif |
242 | | |
243 | 3.60k | o_memory->isMemory64 = (flag & (1u << 2)) != 0; |
244 | 3.60k | if (o_memory->isMemory64) |
245 | 702 | addrBits = 64; |
246 | | |
247 | 3.60k | _ (ReadLebUnsigned (& o_memory->initPages, addrBits, io_bytes, i_end)); |
248 | | |
249 | 3.60k | o_memory->maxPages = 0; |
250 | 3.60k | o_memory->hasMax = (flag & (1u << 0)) != 0; |
251 | 3.60k | if (o_memory->hasMax) |
252 | 810 | { |
253 | 810 | _ (ReadLebUnsigned (& o_memory->maxPages, addrBits, io_bytes, i_end)); |
254 | | |
255 | | // Spec: memory limits validation - max must not be less than init |
256 | 810 | _throwif (m3Err_wasmMalformed, o_memory->maxPages < o_memory->initPages); |
257 | 806 | } |
258 | | |
259 | 3.59k | o_memory->pageSize = 0; |
260 | 3.59k | if (flag & (1u << 3)) { |
261 | 497 | _ (ReadLEB_u32 (& logPageSize, io_bytes, i_end)); |
262 | | |
263 | 496 | _throwif ("invalid custom page size", logPageSize > 16); |
264 | | |
265 | 493 | o_memory->pageSize = 1u << logPageSize; |
266 | 493 | } |
267 | | |
268 | | // Spec: memory limits must be valid within range 2^|addrtype|/pagesize. |
269 | | // That is 65536 pages at the default page size, 2^48 for a 64-bit memory, |
270 | | // and a whole address space of them when a page is a single byte. |
271 | 3.59k | { |
272 | 3.59k | u64 maxPagesAllowed = (logPageSize or addrBits < 64) |
273 | 3.59k | ? (UINT64_C(1) << (addrBits - logPageSize)) |
274 | 3.59k | : UINT64_MAX; |
275 | | |
276 | 3.59k | _throwif (m3Err_wasmMalformed, o_memory->initPages > maxPagesAllowed); |
277 | 3.59k | if (o_memory->hasMax) |
278 | 3.59k | _throwif (m3Err_wasmMalformed, o_memory->maxPages > maxPagesAllowed); |
279 | 3.59k | } |
280 | | |
281 | 3.61k | _catch: return result; |
282 | 3.59k | } |
283 | | |
284 | | |
285 | | M3Result ParseSection_Type (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
286 | 3.42k | { |
287 | 3.42k | IM3FuncType ftype = NULL; |
288 | | |
289 | 3.42k | _try { |
290 | 3.42k | u32 numTypes; |
291 | 3.42k | _ (ReadLEB_u32 (& numTypes, & i_bytes, i_end)); m3log (parse, "** Type [%d]", numTypes); |
292 | | |
293 | 3.42k | _throwif("too many types", numTypes > d_m3MaxSaneTypesCount); |
294 | | |
295 | 3.42k | if (numTypes) |
296 | 3.42k | { |
297 | | // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment) |
298 | 3.42k | io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes); |
299 | 3.42k | _throwifnull (io_module->funcTypes); |
300 | 3.42k | io_module->numFuncTypes = numTypes; |
301 | | |
302 | 7.21k | for (u32 i = 0; i < numTypes; ++i) |
303 | 3.82k | { |
304 | 3.82k | i8 form; |
305 | 3.82k | _ (ReadLEB_i7 (& form, & i_bytes, i_end)); |
306 | 3.81k | _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP |
307 | | |
308 | 3.80k | u32 numArgs; |
309 | 3.80k | _ (ReadLEB_u32 (& numArgs, & i_bytes, i_end)); |
310 | | |
311 | 3.80k | _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount); |
312 | 3.80k | #if M3_HAS_VLA |
313 | 3.80k | m3type_t argTypes[numArgs+1]; // make ubsan happy |
314 | | #else |
315 | | m3type_t argTypes [d_m3MaxSaneFunctionArgRetCount]; |
316 | | #endif |
317 | 8.74k | for (u32 a = 0; a < numArgs; ++a) |
318 | 4.94k | _ (ParseValueType (io_module, & argTypes[a], & i_bytes, i_end)); |
319 | | |
320 | 3.80k | u32 numRets; |
321 | 3.80k | _ (ReadLEB_u32 (& numRets, & i_bytes, i_end)); |
322 | 3.80k | _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount); |
323 | | |
324 | 3.80k | _ (AllocFuncType (& ftype, numRets + numArgs)); |
325 | 3.80k | ftype->numArgs = numArgs; |
326 | 3.80k | ftype->numRets = numRets; |
327 | | |
328 | 12.6k | for (u32 r = 0; r < numRets; ++r) |
329 | 8.82k | _ (ParseValueType (io_module, & ftype->types[r], & i_bytes, i_end)); |
330 | 3.79k | memcpy (ftype->types + numRets, argTypes, numArgs * sizeof (m3type_t)); m3log (parse, " type %2d: %s", i, SPrintFuncTypeSignature (ftype)); |
331 | | |
332 | 3.79k | Environment_AddFuncType (io_module->environment, & ftype); |
333 | 3.79k | io_module->funcTypes [i] = ftype; |
334 | 3.79k | ftype = NULL; // ownership transferred to environment |
335 | 3.79k | } |
336 | 3.42k | } |
337 | | |
338 | 3.39k | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
339 | | |
340 | 3.42k | } _catch: |
341 | | |
342 | 3.42k | if (result) |
343 | 86 | { |
344 | 86 | m3_Free (ftype); |
345 | | // FIX: M3FuncTypes in the table are leaked |
346 | 86 | m3_Free (io_module->funcTypes); |
347 | 86 | io_module->numFuncTypes = 0; |
348 | 86 | } |
349 | | |
350 | 3.42k | return result; |
351 | 3.34k | } |
352 | | |
353 | | |
354 | | M3Result ParseSection_Function (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
355 | 3.28k | { |
356 | 3.28k | M3Result result = m3Err_none; |
357 | | |
358 | 3.28k | u32 numFunctions; |
359 | 3.28k | _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Function [%d]", numFunctions); |
360 | | |
361 | 3.28k | _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount); |
362 | | |
363 | 3.28k | _ (Module_PreallocFunctions(io_module, io_module->numFunctions + numFunctions)); |
364 | | |
365 | 6.92k | for (u32 i = 0; i < numFunctions; ++i) |
366 | 3.63k | { |
367 | 3.63k | u32 funcTypeIndex; |
368 | 3.63k | _ (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end)); |
369 | | |
370 | 3.63k | _ (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */)); |
371 | 3.63k | } |
372 | | |
373 | 3.28k | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
374 | | |
375 | 3.28k | _catch: return result; |
376 | 3.28k | } |
377 | | |
378 | | |
379 | | // One externtype, read but not yet registered. The compact encoding that shares |
380 | | // a type across a run needs the two halves apart: the type sits ahead of the |
381 | | // item names, so it is read once and then registered for each of them. |
382 | | typedef struct M3ImportDesc |
383 | | { |
384 | | u8 kind; |
385 | | u32 typeIndex; // function |
386 | | m3type_t type; // global type |
387 | | u8 isMutable; // global |
388 | | M3TableInfo table; |
389 | | M3MemoryInfo memory; |
390 | | } |
391 | | M3ImportDesc; |
392 | | |
393 | | |
394 | | #if d_m3HasExceptionHandling |
395 | | |
396 | | // A tag_type: a reserved attribute byte that must be 0 (the exception |
397 | | // attribute) and the index of the function type giving the payload. The |
398 | | // proposal reserves the byte so later kinds of tag can share the encoding. |
399 | | static |
400 | | M3Result ReadTagType (IM3Module i_module, u32 * o_typeIndex, bytes_t * io_bytes, cbytes_t i_end) |
401 | 431 | { |
402 | 431 | M3Result result = m3Err_none; |
403 | | |
404 | 431 | u8 attribute; |
405 | 431 | _ (Read_u8 (& attribute, io_bytes, i_end)); |
406 | 431 | _throwif (m3Err_wasmMalformed, attribute != 0); |
407 | | |
408 | 427 | _ (ReadLEB_u32 (o_typeIndex, io_bytes, i_end)); |
409 | 427 | _throwif (m3Err_unknownType, not i_module or * o_typeIndex >= i_module->numFuncTypes); |
410 | | |
411 | | // an exception tag's type describes its payload, so it yields nothing |
412 | 425 | _throwif (m3Err_wasmMalformed, GetFuncTypeNumResults (i_module->funcTypes [* o_typeIndex]) != 0); |
413 | | |
414 | 431 | _catch: return result; |
415 | 425 | } |
416 | | |
417 | | #endif // d_m3HasExceptionHandling |
418 | | |
419 | | |
420 | | // Reads and validates one externtype. Mutates nothing in the module, so an |
421 | | // externtype is checked even when the compact run that shares it is empty. |
422 | | static |
423 | | M3Result ReadImportDesc (IM3Module i_module, M3ImportDesc * o_desc, bytes_t * io_bytes, cbytes_t i_end) |
424 | 2.58k | { |
425 | 2.58k | M3Result result = m3Err_none; |
426 | | |
427 | 2.58k | _ (Read_u8 (& o_desc->kind, io_bytes, i_end)); |
428 | | |
429 | 2.58k | switch (o_desc->kind) |
430 | 2.58k | { |
431 | 2.36k | case d_externalKind_function: |
432 | 2.36k | _ (ReadLEB_u32 (& o_desc->typeIndex, io_bytes, i_end)); |
433 | 2.36k | break; |
434 | | |
435 | 36 | case d_externalKind_table: |
436 | 36 | _ (ReadType_TableType (i_module, & o_desc->table, io_bytes, i_end)); |
437 | 34 | break; |
438 | | |
439 | 44 | case d_externalKind_memory: |
440 | 44 | _ (ParseType_Memory (& o_desc->memory, io_bytes, i_end)); |
441 | 43 | break; |
442 | | |
443 | 54 | case d_externalKind_global: |
444 | 54 | _ (ParseValueType (i_module, & o_desc->type, io_bytes, i_end)); |
445 | 54 | _ (ReadLEB_u7 (& o_desc->isMutable, io_bytes, i_end)); m3log (parse, " global: %s mutable=%d", c_waTypes [BaseTypeOf(o_desc->type)], (u32) o_desc->isMutable); |
446 | 54 | _throwif (m3Err_wasmMalformed, o_desc->isMutable > 1); |
447 | 54 | break; |
448 | | |
449 | 0 | #if d_m3HasExceptionHandling |
450 | 72 | case d_externalKind_tag: |
451 | 72 | _ (ReadTagType (i_module, & o_desc->typeIndex, io_bytes, i_end)); |
452 | 71 | break; |
453 | 0 | #endif |
454 | | |
455 | 7 | default: |
456 | 7 | _throw (m3Err_wasmMalformed); |
457 | 2.58k | } |
458 | | |
459 | 2.58k | _catch: return result; |
460 | 2.58k | } |
461 | | |
462 | | |
463 | | // Registers the import that i_desc describes. Takes ownership of *io_import |
464 | | // wherever the module keeps the strings, clearing the struct so the caller's |
465 | | // FreeImportInfo() doesn't free what was handed over. |
466 | | static |
467 | | M3Result ApplyImportDesc (IM3Module io_module, const M3ImportDesc * i_desc, M3ImportInfo * io_import) |
468 | 5.41k | { |
469 | 5.41k | M3Result result = m3Err_none; |
470 | | |
471 | 5.41k | M3ImportInfo clearImport = { NULL, NULL }; m3log (parse, " kind: %d '%s.%s' ", |
472 | 5.41k | (u32) i_desc->kind, io_import->moduleUtf8, io_import->fieldUtf8); |
473 | 5.41k | switch (i_desc->kind) |
474 | 5.41k | { |
475 | 3.43k | case d_externalKind_function: |
476 | 3.43k | { |
477 | 3.43k | _ (Module_AddFunction (io_module, i_desc->typeIndex, io_import)) |
478 | 3.43k | * io_import = clearImport; |
479 | | |
480 | 3.43k | io_module->numFuncImports++; |
481 | 3.43k | } |
482 | 0 | break; |
483 | | |
484 | 175 | case d_externalKind_table: |
485 | 175 | { |
486 | 175 | IM3Table table; |
487 | 175 | _ (Module_AddTable (io_module, & table, & i_desc->table, true /* isImport */)); |
488 | 175 | table->import = * io_import; |
489 | 175 | * io_import = clearImport; |
490 | 175 | } |
491 | 0 | break; |
492 | | |
493 | 1.19k | case d_externalKind_memory: |
494 | 1.19k | { |
495 | 1.19k | IM3Memory memory; |
496 | | |
497 | 1.19k | #if d_m3HasMultiMemory |
498 | 1.19k | _throwif (m3Err_tooManyMemorySections, io_module->numMemories >= d_m3MaxSaneMemoriesCount); |
499 | | #else |
500 | | // MVP: one memory per module, imported or declared |
501 | | _throwif (m3Err_tooManyMemorySections, io_module->numMemories >= 1); |
502 | | #endif |
503 | | |
504 | 1.19k | _ (Module_AddMemory (io_module, & memory, & i_desc->memory, true /* isImport */)); |
505 | 1.19k | memory->import = * io_import; |
506 | 1.19k | * io_import = clearImport; |
507 | 1.19k | } |
508 | 0 | break; |
509 | | |
510 | 185 | case d_externalKind_global: |
511 | 185 | { |
512 | 185 | IM3Global global; |
513 | 185 | _ (Module_AddGlobal (io_module, & global, i_desc->type, i_desc->isMutable, true /* isImport */)); |
514 | 185 | global->import = * io_import; |
515 | 185 | * io_import = clearImport; |
516 | 185 | } |
517 | 0 | break; |
518 | | |
519 | 0 | #if d_m3HasExceptionHandling |
520 | 420 | case d_externalKind_tag: |
521 | 420 | { |
522 | 420 | IM3Tag tag; |
523 | 420 | _ (Module_AddTag (io_module, & tag, io_module->funcTypes [i_desc->typeIndex], true /* isImport */)); |
524 | 420 | tag->import = * io_import; |
525 | 420 | * io_import = clearImport; |
526 | 420 | } |
527 | 0 | break; |
528 | 0 | #endif |
529 | | |
530 | 0 | default: |
531 | 0 | _throw (m3Err_wasmMalformed); |
532 | 5.41k | } |
533 | | |
534 | 5.41k | _catch: return result; |
535 | 5.41k | } |
536 | | |
537 | | |
538 | | #if d_m3HasCompactImports |
539 | | |
540 | | // The compact encodings only apply when the item name is empty. Read_utf8() |
541 | | // hands back a C string, so a name that merely begins with U+0000 would look |
542 | | // empty; the length prefix is what has to be tested. |
543 | | static |
544 | | bool IsEmptyName (bytes_t i_bytes, cbytes_t i_end) |
545 | 465 | { |
546 | 465 | u32 length; |
547 | 465 | return (not ReadLEB_u32 (& length, & i_bytes, i_end)) and length == 0; |
548 | 465 | } |
549 | | |
550 | | #endif // d_m3HasCompactImports |
551 | | |
552 | | |
553 | | #if d_m3HasExceptionHandling |
554 | | |
555 | | M3Result ParseSection_Tag (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
556 | 5 | { |
557 | 5 | M3Result result = m3Err_none; |
558 | | |
559 | 5 | u32 numTags; |
560 | 5 | _ (ReadLEB_u32 (& numTags, & i_bytes, i_end)); m3log (parse, "** Tag [%d]", numTags); |
561 | | |
562 | 5 | _throwif ("too many tags", numTags > d_m3MaxSaneTagsCount); |
563 | | |
564 | 359 | for (u32 i = 0; i < numTags; ++i) |
565 | 359 | { |
566 | 359 | u32 typeIndex; |
567 | 359 | _ (ReadTagType (io_module, & typeIndex, & i_bytes, i_end)); m3log (parse, " tag: [%d] type: %d", i, typeIndex); |
568 | | |
569 | 354 | _ (Module_AddTag (io_module, NULL, io_module->funcTypes [typeIndex], false)); |
570 | 354 | } |
571 | | |
572 | 0 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
573 | |
|
574 | 5 | _catch: return result; |
575 | 0 | } |
576 | | |
577 | | #endif // d_m3HasExceptionHandling |
578 | | |
579 | | |
580 | | M3Result ParseSection_Import (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
581 | 54 | { |
582 | 54 | M3Result result = m3Err_none; |
583 | | |
584 | 54 | M3ImportInfo import = { NULL, NULL }; |
585 | | |
586 | | // the limit is on the imports the section declares, which a compact run |
587 | | // multiplies well past the entry count. Declared up here so the throws |
588 | | // below don't jump over its initialization. |
589 | 54 | u32 numImports = 0; |
590 | | |
591 | | // A count of entries, not of imports: under the compact encodings a single |
592 | | // entry stands for a whole run of them. |
593 | 54 | u32 numEntries; |
594 | 54 | _ (ReadLEB_u32 (& numEntries, & i_bytes, i_end)); m3log (parse, "** Import [%d]", numEntries); |
595 | | |
596 | 54 | _throwif("too many imports", numEntries > d_m3MaxSaneImportsCount); |
597 | | |
598 | | // Most imports are functions, so we won't waste much space anyway (if any) |
599 | 54 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions + numEntries)); |
600 | | |
601 | 2.16k | for (u32 i = 0; i < numEntries; ++i) |
602 | 2.15k | { |
603 | 2.15k | M3ImportDesc desc; |
604 | | |
605 | 2.15k | _ (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end)); |
606 | | |
607 | 2.15k | #if d_m3HasCompactImports |
608 | 2.15k | bytes_t fieldStart = i_bytes; |
609 | 2.15k | #endif |
610 | 2.15k | _ (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end)); |
611 | | |
612 | 2.14k | #if d_m3HasCompactImports |
613 | 2.14k | u8 compact = (i_bytes < i_end) ? * i_bytes : 0; |
614 | | |
615 | | // An empty item name where an externtype should start marks a compact |
616 | | // run sharing this module name. Neither marker is a valid externtype, |
617 | | // so nothing that used to parse changes meaning. |
618 | 2.14k | if ((compact == d_compactImports_perItemType or compact == d_compactImports_sharedType) and |
619 | 465 | IsEmptyName (fieldStart, i_end)) |
620 | 464 | { |
621 | 464 | ++i_bytes; |
622 | | |
623 | 464 | m3_Free (import.fieldUtf8); // it only marked the compact form |
624 | 464 | import.fieldUtf8 = NULL; |
625 | | |
626 | | // the shared form puts its one externtype ahead of the item names, |
627 | | // so reading it here is also what validates it |
628 | 464 | if (compact == d_compactImports_sharedType) |
629 | 463 | _ (ReadImportDesc (io_module, & desc, & i_bytes, i_end)); |
630 | | |
631 | 463 | u32 numItems; |
632 | 463 | _ (ReadLEB_u32 (& numItems, & i_bytes, i_end)); m3log (parse, " compact: %d import(s) of '%s'", numItems, import.moduleUtf8); |
633 | | |
634 | 463 | _throwif("too many imports", (u64) numImports + numItems > d_m3MaxSaneImportsCount); |
635 | 463 | numImports += numItems; |
636 | | |
637 | 463 | _ (Module_PreallocFunctions(io_module, io_module->numFunctions + numItems)); |
638 | | |
639 | 4.20k | for (u32 j = 0; j < numItems; ++j) |
640 | 3.75k | { |
641 | 3.75k | M3ImportInfo item = { NULL, NULL }; |
642 | | |
643 | 3.75k | item.moduleUtf8 = (cstr_t) m3_CopyMem (import.moduleUtf8, strlen (import.moduleUtf8) + 1); |
644 | | |
645 | 3.75k | if (item.moduleUtf8) |
646 | 3.75k | { |
647 | 3.75k | result = Read_utf8 (& item.fieldUtf8, & i_bytes, i_end); |
648 | | |
649 | | // the shared form reuses the externtype read above |
650 | 3.75k | if (not result and compact == d_compactImports_perItemType) |
651 | 471 | result = ReadImportDesc (io_module, & desc, & i_bytes, i_end); |
652 | | |
653 | 3.75k | if (not result) |
654 | 3.74k | result = ApplyImportDesc (io_module, & desc, & item); |
655 | 3.75k | } |
656 | 0 | else result = m3Err_mallocFailed; |
657 | | |
658 | 3.75k | FreeImportInfo (& item); |
659 | 3.75k | _throwif (result, result); |
660 | 3.74k | } |
661 | | |
662 | 445 | FreeImportInfo (& import); |
663 | 445 | continue; |
664 | 463 | } |
665 | 1.68k | #endif // d_m3HasCompactImports |
666 | | |
667 | 1.68k | _throwif("too many imports", numImports >= d_m3MaxSaneImportsCount); |
668 | 1.68k | ++numImports; |
669 | | |
670 | 1.68k | _ (ReadImportDesc (io_module, & desc, & i_bytes, i_end)); |
671 | 1.67k | _ (ApplyImportDesc (io_module, & desc, & import)); |
672 | | |
673 | 1.66k | FreeImportInfo (& import); |
674 | 1.66k | } |
675 | | |
676 | 12 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
677 | | |
678 | 54 | _catch: |
679 | | |
680 | 54 | FreeImportInfo (& import); |
681 | | |
682 | 54 | return result; |
683 | 12 | } |
684 | | |
685 | | |
686 | | M3Result ParseSection_Export (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
687 | 22 | { |
688 | 22 | M3Result result = m3Err_none; |
689 | 22 | const char * utf8 = NULL; |
690 | 22 | #if d_m3EnableValidation |
691 | | // We store name pointers + lengths to handle embedded NUL bytes correctly |
692 | 22 | typedef struct { const u8 * ptr; u16 len; } ExportName; |
693 | 22 | ExportName * exportNames = NULL; |
694 | 22 | #endif |
695 | | |
696 | 22 | u32 numExports; |
697 | 22 | _ (ReadLEB_u32 (& numExports, & i_bytes, i_end)); m3log (parse, "** Export [%d]", numExports); |
698 | | |
699 | 22 | _throwif("too many exports", numExports > d_m3MaxSaneExportsCount); |
700 | | |
701 | 22 | #if d_m3EnableValidation |
702 | | // Spec: all export names must be different |
703 | 22 | if (numExports > 1) |
704 | 22 | { |
705 | 22 | exportNames = (ExportName *) m3_Malloc ("exportNames", sizeof(ExportName) * numExports); |
706 | 22 | } |
707 | 22 | #endif |
708 | | |
709 | 182 | for (u32 i = 0; i < numExports; ++i) |
710 | 181 | { |
711 | 181 | u8 exportKind; |
712 | 181 | u32 index; |
713 | | |
714 | | // Read name length and remember raw position for uniqueness check |
715 | 181 | #if d_m3EnableValidation |
716 | 181 | const u8 * nameStart = i_bytes; |
717 | 181 | u32 nameLen = 0; |
718 | 181 | { |
719 | 181 | bytes_t tmp = i_bytes; |
720 | 181 | M3Result rl = ReadLEB_u32 (& nameLen, & tmp, i_end); |
721 | 181 | if (rl) { m3_Free(exportNames); _throw(rl); } |
722 | 181 | nameStart = tmp; // points to the raw name bytes |
723 | 181 | } |
724 | 0 | #endif |
725 | | |
726 | 181 | _ (Read_utf8 (& utf8, & i_bytes, i_end)); |
727 | 169 | _ (Read_u8 (& exportKind, & i_bytes, i_end)); |
728 | 169 | _ (ReadLEB_u32 (& index, & i_bytes, i_end)); m3log (parse, " index: %3d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8); |
729 | | |
730 | 168 | #if d_m3EnableValidation |
731 | 168 | if (exportNames) |
732 | 168 | { |
733 | 1.15k | for (u32 j = 0; j < i; ++j) |
734 | 990 | { |
735 | 990 | if (exportNames[j].len == nameLen && |
736 | 391 | memcmp (exportNames[j].ptr, nameStart, nameLen) == 0) |
737 | 6 | { |
738 | 6 | m3_Free (exportNames); |
739 | 6 | _throw (m3Err_wasmMalformed); // duplicate export name |
740 | 0 | } |
741 | 990 | } |
742 | 162 | exportNames[i].ptr = nameStart; |
743 | 162 | exportNames[i].len = (u16)nameLen; |
744 | 162 | } |
745 | 162 | #endif |
746 | | |
747 | 162 | if (exportKind == d_externalKind_function) |
748 | 53 | { |
749 | 53 | _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions); |
750 | 51 | IM3Function func = &(io_module->functions [index]); |
751 | 51 | if (func->numNames < d_m3MaxDuplicateFunctionImpl) |
752 | 37 | { |
753 | 37 | func->names[func->numNames++] = utf8; |
754 | 37 | func->export_name = utf8; |
755 | 37 | utf8 = NULL; // ownership transferred to M3Function |
756 | 37 | } |
757 | 51 | _ (Module_DeclareFunction (io_module, index)); |
758 | 51 | } |
759 | 109 | else if (exportKind == d_externalKind_global) |
760 | 0 | { |
761 | 0 | _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals); |
762 | 0 | IM3Global global = &(io_module->globals [index]); |
763 | 0 | m3_Free (global->name); |
764 | 0 | global->name = utf8; |
765 | 0 | utf8 = NULL; // ownership transferred to M3Global |
766 | 0 | } |
767 | 109 | else if (exportKind == d_externalKind_memory) |
768 | 0 | { |
769 | 0 | _throwif(m3Err_wasmMalformed, index >= io_module->numMemories); |
770 | 0 | IM3Memory memory = io_module->memories [index]; |
771 | 0 | m3_Free (memory->exportName); |
772 | 0 | memory->exportName = utf8; |
773 | 0 | utf8 = NULL; // ownership transferred to M3Memory |
774 | 0 | } |
775 | 109 | else if (exportKind == d_externalKind_table) |
776 | 0 | { |
777 | 0 | _throwif(m3Err_wasmMalformed, index >= io_module->numTables); |
778 | 0 | IM3Table table = io_module->tables [index]; |
779 | 0 | m3_Free (table->exportName); |
780 | 0 | table->exportName = utf8; |
781 | 0 | utf8 = NULL; // ownership transferred to M3Table |
782 | 0 | } |
783 | 109 | #if d_m3HasExceptionHandling |
784 | 109 | else if (exportKind == d_externalKind_tag) |
785 | 0 | { |
786 | 0 | _throwif(m3Err_wasmMalformed, index >= io_module->numTags); |
787 | 0 | IM3Tag tag = &(io_module->tags [index]); |
788 | 0 | m3_Free (tag->name); |
789 | 0 | tag->name = utf8; |
790 | 0 | utf8 = NULL; // ownership transferred to M3Tag |
791 | 0 | } |
792 | 160 | #endif |
793 | | |
794 | 160 | m3_Free (utf8); |
795 | 160 | } |
796 | | |
797 | 1 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
798 | |
|
799 | 22 | _catch: |
800 | 22 | m3_Free (utf8); |
801 | 22 | #if d_m3EnableValidation |
802 | 22 | m3_Free (exportNames); |
803 | 22 | #endif |
804 | 22 | return result; |
805 | 0 | } |
806 | | |
807 | | |
808 | | M3Result ParseSection_Start (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
809 | 0 | { |
810 | 0 | M3Result result = m3Err_none; |
811 | |
|
812 | 0 | u32 startFuncIndex; |
813 | 0 | _ (ReadLEB_u32 (& startFuncIndex, & i_bytes, i_end)); m3log (parse, "** Start Function: %d", startFuncIndex); |
814 | |
|
815 | 0 | if (startFuncIndex < io_module->numFunctions) |
816 | 0 | { |
817 | | // Spec: start function type must be [] -> [] |
818 | 0 | IM3Function func = & io_module->functions [startFuncIndex]; |
819 | 0 | if (func->funcType) |
820 | 0 | { |
821 | 0 | _throwif (m3Err_wasmMalformed, |
822 | 0 | func->funcType->numArgs != 0 || func->funcType->numRets != 0); |
823 | 0 | } |
824 | | |
825 | 0 | io_module->startFunction = startFuncIndex; |
826 | 0 | } |
827 | 0 | else result = "start function index out of bounds"; |
828 | | |
829 | 0 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
830 | |
|
831 | 0 | _catch: return result; |
832 | 0 | } |
833 | | |
834 | | |
835 | | // o_type, when asked for, reports what the expression leaves on the stack, so |
836 | | // the caller can check it against the type the expression is initializing. |
837 | | M3Result Parse_InitExprTyped (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end, m3type_t * o_type) |
838 | 186 | { |
839 | 186 | M3Result result = m3Err_none; |
840 | | |
841 | | // this doesn't generate code pages. just walks the wasm bytecode to find the end |
842 | | |
843 | | #if defined(d_m3PreferStaticAlloc) |
844 | | static M3Compilation compilation; |
845 | | #else |
846 | 186 | M3Compilation compilation; |
847 | 186 | #endif |
848 | 186 | compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end, .isInitExpr = true }; |
849 | | |
850 | 186 | result = CompileBlockStatements (& compilation); |
851 | | |
852 | 186 | * io_bytes = compilation.wasm; |
853 | | |
854 | 186 | if (o_type) |
855 | 0 | { |
856 | 0 | * o_type = compilation.stackIndex ? compilation.typeStack [compilation.stackIndex - 1] |
857 | 0 | : (m3type_t) c_m3Type_none; |
858 | 0 | } |
859 | | |
860 | 186 | return result; |
861 | 186 | } |
862 | | |
863 | | |
864 | | M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end) |
865 | 186 | { |
866 | 186 | return Parse_InitExprTyped (io_module, io_bytes, i_end, NULL); |
867 | 186 | } |
868 | | |
869 | | |
870 | | M3Result ParseSection_Element (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) |
871 | 20 | { |
872 | 20 | M3Result result = m3Err_none; |
873 | | |
874 | 20 | u32 numSegments; |
875 | 20 | bytes_t pos; |
876 | 20 | _ (ReadLEB_u32 (& numSegments, & i_bytes, i_end)); m3log (parse, "** Element [%d]", numSegments); |
877 | | |
878 | 20 | _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments); |
879 | | |
880 | 20 | io_module->elementSegments = m3_AllocArray (M3ElementSegment, numSegments); |
881 | 20 | _throwifnull (io_module->elementSegments); |
882 | 20 | io_module->numElementSegments = numSegments; |
883 | 20 | io_module->elementSectionEnd = i_end; |
884 | | |
885 | | // Records where each segment's parts live; the elements are resolved later, |
886 | | // in InitTableAndElements. |
887 | 20 | pos = i_bytes; |
888 | 48 | for (u32 i = 0; i < numSegments; ++i) |
889 | 36 | { |
890 | 36 | M3ElementSegment * segment = & io_module->elementSegments [i]; |
891 | | |
892 | 36 | u32 flags; |
893 | 36 | _ (ReadLEB_u32 (& flags, & pos, i_end)); |
894 | 36 | #if d_m3HasRefTypes |
895 | 36 | _throwif (m3Err_wasmMalformed, flags > 7); |
896 | | #else |
897 | | _throwif (m3Err_wasmMalformed, flags != c_m3Elem_active); |
898 | | #endif |
899 | | |
900 | 35 | segment->mode = flags & 0x3; |
901 | 35 | segment->isExpr = (flags & 0x4) != 0; |
902 | 35 | segment->type = c_m3Type_funcref; |
903 | | |
904 | 35 | if (segment->mode == c_m3Elem_activeIdx) |
905 | 35 | _ (ReadLEB_u32 (& segment->tableIndex, & pos, i_end)); |
906 | | |
907 | 35 | bool isActive = (segment->mode == c_m3Elem_active or segment->mode == c_m3Elem_activeIdx); |
908 | | |
909 | 35 | if (isActive) |
910 | 7 | { |
911 | 7 | _throwif (m3Err_wasmMalformed, segment->tableIndex >= io_module->numTables); |
912 | | |
913 | 2 | segment->initExpr = pos; |
914 | 2 | _ (Parse_InitExpr (io_module, & pos, i_end)); |
915 | 1 | segment->initExprSize = (u32) (pos - segment->initExpr); |
916 | 1 | _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1); |
917 | 1 | } |
918 | | |
919 | | // Only the table-0 active form leaves the element type implicit |
920 | 29 | if (segment->mode != c_m3Elem_active) |
921 | 28 | { |
922 | 28 | if (segment->isExpr) |
923 | 16 | { |
924 | 16 | _ (ParseValueType (io_module, & segment->type, & pos, i_end)); |
925 | 16 | _throwif (m3Err_wasmMalformed, not IsRefType (BaseTypeOf(segment->type))); |
926 | 16 | } |
927 | 12 | else |
928 | 12 | { |
929 | 12 | u8 elemKind; |
930 | 12 | _ (Read_u8 (& elemKind, & pos, i_end)); |
931 | 12 | _throwif (m3Err_wasmMalformed, elemKind != 0x00); // funcref |
932 | 12 | } |
933 | 28 | } |
934 | | |
935 | 29 | if (isActive) |
936 | 28 | _throwif (m3Err_typeMismatch, io_module->tables [segment->tableIndex]->type != segment->type); |
937 | | |
938 | 28 | _ (ReadLEB_u32 (& segment->numElements, & pos, i_end)); |
939 | 28 | _throwif ("table overflow", segment->numElements > d_m3MaxSaneTableSize); |
940 | | |
941 | 28 | segment->elements = pos; |
942 | | |
943 | 40 | for (u32 e = 0; e < segment->numElements; ++e) |
944 | 12 | { |
945 | 12 | if (segment->isExpr) |
946 | 0 | _ (Parse_InitExpr (io_module, & pos, i_end)) |
947 | 12 | else |
948 | 12 | { |
949 | 12 | u32 funcIndex; |
950 | 12 | _ (ReadLEB_u32 (& funcIndex, & pos, i_end)); |
951 | 12 | _throwif ("function index out of range", funcIndex >= io_module->numFunctions); |
952 | 12 | _ (Module_DeclareFunction (io_module, funcIndex)); |
953 | 12 | } |
954 | 12 | } |
955 | 28 | } |
956 | | |
957 | 12 | _throwif (m3Err_wasmMalformed, pos != i_end); // section size mismatch |
958 | | |
959 | 20 | _catch: return result; |
960 | 12 | } |
961 | | |
962 | | |
963 | | M3Result ParseSection_Code (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
964 | 3.26k | { |
965 | 3.26k | M3Result result; |
966 | | |
967 | 3.26k | u32 numFunctions; |
968 | 3.26k | _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Code [%d]", numFunctions); |
969 | | |
970 | 3.26k | if (numFunctions != io_module->numFunctions - io_module->numFuncImports) |
971 | 1 | { |
972 | 1 | _throw ("mismatched function count in code section"); |
973 | 0 | } |
974 | | |
975 | 6.59k | for (u32 f = 0; f < numFunctions; ++f) |
976 | 3.33k | { |
977 | 3.33k | const u8 * start = i_bytes; |
978 | | |
979 | 3.33k | u32 size; |
980 | 3.33k | _ (ReadLEB_u32 (& size, & i_bytes, i_end)); |
981 | | |
982 | 3.33k | if (size) |
983 | 3.26k | { |
984 | 3.26k | i_bytes += size; |
985 | | |
986 | 3.26k | if (i_bytes <= i_end) |
987 | 3.26k | { |
988 | | /* |
989 | | const u8 * ptr = i_bytes - size; |
990 | | |
991 | | u32 numLocalBlocks; |
992 | | _ (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end)); m3log (parse, " code size: %-4d", size); |
993 | | |
994 | | u32 numLocals = 0; |
995 | | |
996 | | for (u32 l = 0; l < numLocalBlocks; ++l) |
997 | | { |
998 | | u32 varCount; |
999 | | i8 wasmType; |
1000 | | u8 normalType; |
1001 | | |
1002 | | _ (ReadLEB_u32 (& varCount, & ptr, i_end)); |
1003 | | _ (ReadLEB_i7 (& wasmType, & ptr, i_end)); |
1004 | | _ (NormalizeType (& normalType, wasmType)); |
1005 | | |
1006 | | numLocals += varCount; m3log (parse, " %2d locals; type: '%s'", varCount, c_waTypes [normalType]); |
1007 | | } |
1008 | | */ |
1009 | | |
1010 | 3.26k | IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports); |
1011 | | |
1012 | 3.26k | func->module = io_module; |
1013 | 3.26k | func->wasm = start; |
1014 | 3.26k | func->wasmEnd = i_bytes; |
1015 | | //func->ownsWasmCode = io_module->hasWasmCodeCopy; |
1016 | | // func->numLocals = numLocals; |
1017 | 3.26k | } |
1018 | 3.26k | else _throw (m3Err_wasmSectionOverrun); |
1019 | 3.26k | } |
1020 | 3.33k | } |
1021 | | |
1022 | 3.26k | _catch: |
1023 | | |
1024 | 3.26k | if (not result and i_bytes != i_end) |
1025 | 0 | result = m3Err_wasmSectionUnderrun; |
1026 | | |
1027 | 3.26k | return result; |
1028 | 3.26k | } |
1029 | | |
1030 | | |
1031 | | M3Result ParseSection_Data (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
1032 | 20 | { |
1033 | 20 | M3Result result = m3Err_none; |
1034 | | |
1035 | 20 | u32 numDataSegments; |
1036 | 20 | _ (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end)); m3log (parse, "** Data [%d]", numDataSegments); |
1037 | | |
1038 | 20 | _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments); |
1039 | | |
1040 | 20 | io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments); |
1041 | 20 | _throwifnull(io_module->dataSegments); |
1042 | 20 | io_module->numDataSegments = numDataSegments; |
1043 | | |
1044 | 84 | for (u32 i = 0; i < numDataSegments; ++i) |
1045 | 81 | { |
1046 | 81 | M3DataSegment * segment = & io_module->dataSegments [i]; |
1047 | | |
1048 | | // 0: active in memory 0, 1: passive, 2: active with an explicit memory index |
1049 | 81 | u32 flags; |
1050 | 81 | _ (ReadLEB_u32 (& flags, & i_bytes, i_end)); |
1051 | 78 | _throwif (m3Err_wasmMalformed, flags > 2); |
1052 | | |
1053 | 66 | segment->isPassive = (flags == 1); |
1054 | | |
1055 | 66 | if (flags == 2) |
1056 | 5 | { |
1057 | 5 | _ (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end)); |
1058 | 5 | } |
1059 | | |
1060 | 66 | if (not segment->isPassive) |
1061 | 23 | { |
1062 | | // The segment names the memory it initializes; only multi-memory |
1063 | | // lets that be anything other than 0. |
1064 | | #if ! d_m3HasMultiMemory |
1065 | | _throwif (m3Err_wasmMalformed, segment->memoryRegion != 0); |
1066 | | #endif |
1067 | 23 | _throwif (m3Err_wasmMalformed, segment->memoryRegion >= io_module->numMemories); |
1068 | | |
1069 | 22 | segment->initExpr = i_bytes; |
1070 | 22 | _ (Parse_InitExpr (io_module, & i_bytes, i_end)); |
1071 | 21 | segment->initExprSize = (u32) (i_bytes - segment->initExpr); |
1072 | | |
1073 | 21 | _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1); |
1074 | 21 | } |
1075 | | |
1076 | 64 | _ (ReadLEB_u32 (& segment->size, & i_bytes, i_end)); |
1077 | 64 | segment->data = i_bytes; m3log (parse, " segment [%u] memory: %u; expr-size: %d; size: %d", |
1078 | 64 | i, segment->memoryRegion, segment->initExprSize, segment->size); |
1079 | 64 | i_bytes += segment->size; |
1080 | | |
1081 | 64 | _throwif("data segment underflow", i_bytes > i_end); |
1082 | 64 | } |
1083 | | |
1084 | 3 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
1085 | | |
1086 | 20 | _catch: |
1087 | | |
1088 | 20 | return result; |
1089 | 2 | } |
1090 | | |
1091 | | |
1092 | | M3Result ParseSection_DataCount (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
1093 | 4 | { |
1094 | 4 | M3Result result = m3Err_none; |
1095 | | |
1096 | 4 | _ (ReadLEB_u32 (& io_module->dataCount, & i_bytes, i_end)); m3log (parse, "** DataCount [%d]", io_module->dataCount); |
1097 | | |
1098 | 4 | io_module->hasDataCount = true; |
1099 | | |
1100 | 4 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
1101 | | |
1102 | 4 | _catch: return result; |
1103 | 4 | } |
1104 | | |
1105 | | |
1106 | | M3Result ParseSection_Memory (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
1107 | 633 | { |
1108 | 633 | M3Result result = m3Err_none; |
1109 | | |
1110 | | // TODO: MVP; assert no memory imported |
1111 | | |
1112 | 633 | u32 numMemories; |
1113 | 633 | _ (ReadLEB_u32 (& numMemories, & i_bytes, i_end)); m3log (parse, "** Memory [%d]", numMemories); |
1114 | | |
1115 | 633 | #if d_m3HasMultiMemory |
1116 | 633 | _throwif (m3Err_tooManyMemorySections, |
1117 | 633 | (u64) numMemories + io_module->numMemories > d_m3MaxSaneMemoriesCount); |
1118 | | #else |
1119 | | // MVP: at most one memory, counting any that was already imported |
1120 | | _throwif (m3Err_tooManyMemorySections, numMemories > 1); |
1121 | | _throwif (m3Err_tooManyMemorySections, (u64) numMemories + io_module->numMemories > 1); |
1122 | | #endif |
1123 | | |
1124 | 4.18k | for (u32 i = 0; i < numMemories; ++i) |
1125 | 3.57k | { |
1126 | 3.57k | M3MemoryInfo info; |
1127 | 3.57k | _ (ParseType_Memory (& info, & i_bytes, i_end)); |
1128 | 3.54k | _ (Module_AddMemory (io_module, NULL, & info, false /* isImport */)); |
1129 | 3.54k | } |
1130 | | |
1131 | 610 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
1132 | | |
1133 | 633 | _catch: return result; |
1134 | 610 | } |
1135 | | |
1136 | | |
1137 | | M3Result ParseSection_Global (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
1138 | 155 | { |
1139 | 155 | M3Result result = m3Err_none; |
1140 | | |
1141 | 155 | u32 numGlobals; |
1142 | 155 | _ (ReadLEB_u32 (& numGlobals, & i_bytes, i_end)); m3log (parse, "** Global [%d]", numGlobals); |
1143 | | |
1144 | 155 | _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount); |
1145 | | |
1146 | 285 | for (u32 i = 0; i < numGlobals; ++i) |
1147 | 174 | { |
1148 | 174 | m3type_t type = c_m3Type_none; |
1149 | 174 | u8 isMutable; |
1150 | | |
1151 | 174 | _ (ParseValueType (io_module, & type, & i_bytes, i_end)); |
1152 | 172 | _ (ReadLEB_u7 (& isMutable, & i_bytes, i_end)); m3log (parse, " global: [%d] %s mutable: %d", i, c_waTypes [BaseTypeOf(type)], (u32) isMutable); |
1153 | 172 | _throwif (m3Err_wasmMalformed, isMutable > 1); |
1154 | | |
1155 | 162 | IM3Global global; |
1156 | 162 | _ (Module_AddGlobal (io_module, & global, type, isMutable, false /* isImport */)); |
1157 | | |
1158 | 162 | global->initExpr = i_bytes; |
1159 | 162 | _ (Parse_InitExpr (io_module, & i_bytes, i_end)); |
1160 | 130 | global->initExprSize = (u32) (i_bytes - global->initExpr); |
1161 | | |
1162 | 130 | _throwif (m3Err_wasmMissingInitExpr, global->initExprSize <= 1); |
1163 | 130 | } |
1164 | | |
1165 | 111 | _throwif (m3Err_wasmMalformed, i_bytes != i_end); // section size mismatch |
1166 | | |
1167 | 155 | _catch: return result; |
1168 | 105 | } |
1169 | | |
1170 | | |
1171 | | M3Result ParseSection_Name (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
1172 | 3.75k | { |
1173 | 3.75k | M3Result result = m3Err_none; |
1174 | | |
1175 | 3.75k | cstr_t name; |
1176 | | |
1177 | 8.93k | while (i_bytes < i_end) |
1178 | 5.21k | { |
1179 | 5.21k | u8 nameType; |
1180 | 5.21k | u32 payloadLength; |
1181 | | |
1182 | 5.21k | _ (ReadLEB_u7 (& nameType, & i_bytes, i_end)); |
1183 | 5.21k | _ (ReadLEB_u32 (& payloadLength, & i_bytes, i_end)); |
1184 | | |
1185 | 5.21k | bytes_t start = i_bytes; |
1186 | 5.21k | if (nameType == 1) |
1187 | 3.91k | { |
1188 | 3.91k | u32 numNames; |
1189 | 3.91k | _ (ReadLEB_u32 (& numNames, & i_bytes, i_end)); |
1190 | | |
1191 | 3.91k | _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount); |
1192 | | |
1193 | 10.8k | for (u32 i = 0; i < numNames; ++i) |
1194 | 7.02k | { |
1195 | 7.02k | u32 index; |
1196 | 7.02k | _ (ReadLEB_u32 (& index, & i_bytes, i_end)); |
1197 | 7.01k | _ (Read_utf8 (& name, & i_bytes, i_end)); |
1198 | | |
1199 | 6.98k | if (index < io_module->numFunctions) |
1200 | 3.61k | { |
1201 | 3.61k | IM3Function func = &(io_module->functions [index]); |
1202 | 3.61k | if (func->numNames == 0) |
1203 | 3.26k | { |
1204 | 3.26k | func->names[0] = name; m3log (parse, " naming function%5d: %s", index, name); |
1205 | 3.26k | func->numNames = 1; |
1206 | 3.26k | name = NULL; // transfer ownership |
1207 | 3.26k | } |
1208 | | // else m3log (parse, "prenamed: %s", io_module->functions [index].name); |
1209 | 3.61k | } |
1210 | | |
1211 | 6.98k | m3_Free (name); |
1212 | 6.98k | } |
1213 | 3.90k | } |
1214 | | |
1215 | 5.18k | i_bytes = start + payloadLength; |
1216 | 5.18k | } |
1217 | | |
1218 | 3.75k | _catch: return result; |
1219 | 3.75k | } |
1220 | | |
1221 | | |
1222 | | M3Result ParseSection_Custom (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end) |
1223 | 4.72k | { |
1224 | 4.72k | M3Result result = m3Err_none; |
1225 | | |
1226 | 4.72k | cstr_t name = NULL; |
1227 | 4.72k | _ (Read_utf8 (& name, & i_bytes, i_end)); |
1228 | 4.71k | m3log (parse, "** Custom: '%s'", name); |
1229 | 4.71k | if (strcmp (name, "name") == 0) { |
1230 | 3.75k | _ (ParseSection_Name(io_module, i_bytes, i_end)); |
1231 | 3.71k | } else if (io_module->environment->customSectionHandler) { |
1232 | 0 | _ (io_module->environment->customSectionHandler(io_module, name, i_bytes, i_end)); |
1233 | 0 | } |
1234 | | |
1235 | | // the section name is freed on the error path too: a malformed "name" |
1236 | | // section, or a handler that rejects the payload, still throws past here |
1237 | 4.72k | _catch: |
1238 | | |
1239 | 4.72k | m3_Free (name); |
1240 | | |
1241 | 4.72k | return result; |
1242 | 4.71k | } |
1243 | | |
1244 | | |
1245 | | M3Result ParseModuleSection (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes) |
1246 | 15.8k | { |
1247 | 15.8k | M3Result result = m3Err_none; |
1248 | | |
1249 | 15.8k | typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t); |
1250 | | |
1251 | 15.8k | static M3Parser s_parsers [] = |
1252 | 15.8k | { |
1253 | 15.8k | ParseSection_Custom, // 0 |
1254 | 15.8k | ParseSection_Type, // 1 |
1255 | 15.8k | ParseSection_Import, // 2 |
1256 | 15.8k | ParseSection_Function, // 3 |
1257 | 15.8k | ParseType_Table, // 4 |
1258 | 15.8k | ParseSection_Memory, // 5 |
1259 | 15.8k | ParseSection_Global, // 6 |
1260 | 15.8k | ParseSection_Export, // 7 |
1261 | 15.8k | ParseSection_Start, // 8 |
1262 | 15.8k | ParseSection_Element, // 9 |
1263 | 15.8k | ParseSection_Code, // 10 |
1264 | 15.8k | ParseSection_Data, // 11 |
1265 | 15.8k | ParseSection_DataCount, // 12 |
1266 | 15.8k | #if d_m3HasExceptionHandling |
1267 | 15.8k | ParseSection_Tag, // 13 |
1268 | 15.8k | #endif |
1269 | 15.8k | }; |
1270 | | |
1271 | 15.8k | M3Parser parser = NULL; |
1272 | | |
1273 | 15.8k | if (i_sectionType < M3_COUNT_OF (s_parsers)) |
1274 | 15.8k | parser = s_parsers [i_sectionType]; |
1275 | | |
1276 | 15.8k | if (parser) |
1277 | 15.8k | { |
1278 | 15.8k | cbytes_t end = i_bytes + i_numBytes; |
1279 | 15.8k | result = parser (o_module, i_bytes, end); |
1280 | 15.8k | } |
1281 | 0 | else |
1282 | 0 | { |
1283 | 0 | m3log (parse, " skipped section type: %d", (u32) i_sectionType); |
1284 | 0 | } |
1285 | | |
1286 | 15.8k | return result; |
1287 | 15.8k | } |
1288 | | |
1289 | | |
1290 | | M3Result m3_ParseModule (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes) |
1291 | 3.67k | { |
1292 | 3.67k | IM3Module module; m3log (parse, "load module: %d bytes", i_numBytes); |
1293 | 3.67k | _try { |
1294 | 3.67k | module = m3_AllocStruct (M3Module); |
1295 | 3.67k | _throwifnull (module); |
1296 | 3.67k | module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes); |
1297 | 3.67k | module->startFunction = -1; |
1298 | | //module->hasWasmCodeCopy = false; |
1299 | 3.67k | module->environment = i_environment; |
1300 | | |
1301 | 3.67k | const u8 * pos = i_bytes; |
1302 | 3.67k | const u8 * end = pos + i_numBytes; |
1303 | | |
1304 | 3.67k | module->wasmStart = pos; |
1305 | 3.67k | module->wasmEnd = end; |
1306 | | |
1307 | 3.67k | u32 magic, version; |
1308 | 3.67k | _ (Read_u32 (& magic, & pos, end)); |
1309 | 3.67k | _ (Read_u32 (& version, & pos, end)); |
1310 | | |
1311 | 3.67k | _throwif (m3Err_wasmMalformed, magic != 0x6d736100); |
1312 | 3.67k | _throwif (m3Err_incompatibleWasmVersion, version != 1); |
1313 | | |
1314 | 3.67k | #if d_m3HasExceptionHandling |
1315 | | // the tag section sits between memory (5) and global (6) |
1316 | 3.67k | static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 13, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder |
1317 | | #else |
1318 | | static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder |
1319 | | #endif |
1320 | 3.67k | u8 expectedSection = 0; |
1321 | | |
1322 | 19.2k | while (pos < end) |
1323 | 15.9k | { |
1324 | 15.9k | u8 section; |
1325 | 15.9k | _ (ReadLEB_u7 (& section, & pos, end)); |
1326 | | |
1327 | 15.9k | if (section != 0) { |
1328 | | // Ensure sections appear only once and in order |
1329 | 40.8k | while (sectionsOrder[expectedSection++] != section) { |
1330 | 29.7k | _throwif(m3Err_misorderedWasmSection, expectedSection >= M3_COUNT_OF (sectionsOrder) - 1); |
1331 | 29.7k | } |
1332 | 11.1k | } |
1333 | | |
1334 | 15.8k | u32 sectionLength; |
1335 | 15.8k | _ (ReadLEB_u32 (& sectionLength, & pos, end)); |
1336 | 15.8k | _throwif(m3Err_wasmMalformed, pos + sectionLength > end); |
1337 | | |
1338 | 15.8k | _ (ParseModuleSection (module, section, pos, sectionLength)); |
1339 | | |
1340 | 15.5k | pos += sectionLength; |
1341 | 15.5k | } |
1342 | | |
1343 | | // Spec: if a function section exists, a code section must also exist with |
1344 | | // matching count (and vice versa). ParseSection_Code checks the other |
1345 | | // direction; this covers the case where the code section is missing entirely. |
1346 | 3.33k | if (module->numFunctions > module->numFuncImports) |
1347 | 3.25k | { |
1348 | 3.25k | IM3Function firstNonImport = & module->functions [module->numFuncImports]; |
1349 | 3.25k | _throwif (m3Err_wasmMalformed, firstNonImport->wasm == NULL); |
1350 | 3.24k | } |
1351 | | |
1352 | | // Spec: the data count section must agree with the data section, which may be absent |
1353 | 3.32k | _throwif (m3Err_wasmMalformed, module->hasDataCount and module->dataCount != module->numDataSegments); |
1354 | | |
1355 | 3.67k | } _catch: |
1356 | | |
1357 | 3.67k | if (result) |
1358 | 355 | { |
1359 | 355 | m3_FreeModule (module); |
1360 | 355 | module = NULL; |
1361 | 355 | } |
1362 | | |
1363 | 3.67k | * o_module = module; |
1364 | | |
1365 | 3.67k | return result; |
1366 | 3.32k | } |