1 //
2 // m3_core.c
3 //
4 // Created by Steven Massey on 4/15/19.
5 // Copyright © 2019 Steven Massey. All rights reserved.
6 //
7
8 #define M3_IMPLEMENT_ERROR_STRINGS
9 #include "wasm3.h"
10
11 #include "m3_core.h"
12 #include "m3_env.h"
13
m3_Abort(const char * message)14 void m3_Abort(const char* message) {
15 #ifdef DEBUG
16 fprintf(stderr, "Error: %s\n", message);
17 #endif
18 abort();
19 }
20
21 M3_WEAK
m3_Yield()22 M3Result m3_Yield ()
23 {
24 return m3Err_none;
25 }
26
27 #if d_m3FixedHeap
28
29 static u8 fixedHeap[d_m3FixedHeap];
30 static u8* fixedHeapPtr = fixedHeap;
31 static u8* const fixedHeapEnd = fixedHeap + d_m3FixedHeap;
32 static u8* fixedHeapLast = NULL;
33
34 #if d_m3FixedHeapAlign > 1
35 # define HEAP_ALIGN_PTR(P) P = (u8*)(((size_t)(P)+(d_m3FixedHeapAlign-1)) & ~ (d_m3FixedHeapAlign-1));
36 #else
37 # define HEAP_ALIGN_PTR(P)
38 #endif
39
m3_Malloc(size_t i_size)40 void * m3_Malloc (size_t i_size)
41 {
42 u8 * ptr = fixedHeapPtr;
43
44 fixedHeapPtr += i_size;
45 HEAP_ALIGN_PTR(fixedHeapPtr);
46
47 if (fixedHeapPtr >= fixedHeapEnd)
48 {
49 return NULL;
50 }
51
52 memset (ptr, 0x0, i_size);
53 fixedHeapLast = ptr;
54
55 //printf("== alloc %d => %p\n", i_size, ptr);
56
57 return ptr;
58 }
59
m3_FreeImpl(void * i_ptr)60 void m3_FreeImpl (void * i_ptr)
61 {
62 // Handle the last chunk
63 if (i_ptr && i_ptr == fixedHeapLast) {
64 fixedHeapPtr = fixedHeapLast;
65 fixedHeapLast = NULL;
66 //printf("== free %p\n", io_ptr);
67 } else {
68 //printf("== free %p [failed]\n", io_ptr);
69 }
70 }
71
m3_Realloc(void * i_ptr,size_t i_newSize,size_t i_oldSize)72 void * m3_Realloc (void * i_ptr, size_t i_newSize, size_t i_oldSize)
73 {
74 //printf("== realloc %p => %d\n", io_ptr, i_newSize);
75
76 if (UNLIKELY(i_newSize == i_oldSize)) return i_ptr;
77
78 void * newPtr;
79
80 // Handle the last chunk
81 if (i_ptr && i_ptr == fixedHeapLast) {
82 fixedHeapPtr = fixedHeapLast + i_newSize;
83 HEAP_ALIGN_PTR(fixedHeapPtr);
84 if (fixedHeapPtr >= fixedHeapEnd)
85 {
86 return NULL;
87 }
88 newPtr = i_ptr;
89 } else {
90 newPtr = m3_Malloc(i_newSize);
91 if (!newPtr) {
92 return NULL;
93 }
94 if (i_ptr) {
95 memcpy(newPtr, i_ptr, i_oldSize);
96 }
97 }
98
99 if (i_newSize > i_oldSize) {
100 memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize);
101 }
102
103 return newPtr;
104 }
105
106 #else
107
m3_Malloc(size_t i_size)108 void * m3_Malloc (size_t i_size)
109 {
110 void * ptr = calloc (i_size, 1);
111
112 // printf("== alloc %d => %p\n", (u32) i_size, ptr);
113
114 return ptr;
115 }
116
m3_FreeImpl(void * io_ptr)117 void m3_FreeImpl (void * io_ptr)
118 {
119 // if (io_ptr) printf("== free %p\n", io_ptr);
120 free (io_ptr);
121 }
122
m3_Realloc(void * i_ptr,size_t i_newSize,size_t i_oldSize)123 void * m3_Realloc (void * i_ptr, size_t i_newSize, size_t i_oldSize)
124 {
125 if (UNLIKELY(i_newSize == i_oldSize)) return i_ptr;
126
127 void * newPtr = realloc (i_ptr, i_newSize);
128
129 if (LIKELY(newPtr))
130 {
131 if (i_newSize > i_oldSize) {
132 memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize);
133 }
134 return newPtr;
135 }
136 return NULL;
137 }
138
139 #endif
140
m3_CopyMem(const void * i_from,size_t i_size)141 void * m3_CopyMem (const void * i_from, size_t i_size)
142 {
143 void * ptr = m3_Malloc(i_size);
144 if (ptr) {
145 memcpy (ptr, i_from, i_size);
146 }
147 return ptr;
148 }
149
150 //--------------------------------------------------------------------------------------------
151
152 #if d_m3LogNativeStack
153
154 static size_t stack_start;
155 static size_t stack_end;
156
m3StackCheckInit()157 void m3StackCheckInit ()
158 {
159 char stack;
160 stack_end = stack_start = (size_t)&stack;
161 }
162
m3StackCheck()163 void m3StackCheck ()
164 {
165 char stack;
166 size_t addr = (size_t)&stack;
167
168 size_t stackEnd = stack_end;
169 stack_end = M3_MIN (stack_end, addr);
170
171 // if (stackEnd != stack_end)
172 // printf ("maxStack: %ld\n", m3StackGetMax ());
173 }
174
m3StackGetMax()175 int m3StackGetMax ()
176 {
177 return stack_start - stack_end;
178 }
179
180 #endif
181
182 //--------------------------------------------------------------------------------------------
183
NormalizeType(u8 * o_type,i8 i_convolutedWasmType)184 M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType)
185 {
186 M3Result result = m3Err_none;
187
188 u8 type = -i_convolutedWasmType;
189
190 if (type == 0x40)
191 type = c_m3Type_none;
192 else if (type < c_m3Type_i32 or type > c_m3Type_f64)
193 result = m3Err_invalidTypeId;
194
195 * o_type = type;
196
197 return result;
198 }
199
200
IsFpType(u8 i_m3Type)201 bool IsFpType (u8 i_m3Type)
202 {
203 return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64);
204 }
205
206
IsIntType(u8 i_m3Type)207 bool IsIntType (u8 i_m3Type)
208 {
209 return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64);
210 }
211
212
Is64BitType(u8 i_m3Type)213 bool Is64BitType (u8 i_m3Type)
214 {
215 if (i_m3Type == c_m3Type_i64 or i_m3Type == c_m3Type_f64)
216 return true;
217 else if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_none)
218 return false;
219 else
220 return (sizeof (voidptr_t) == 8); // all other cases are pointers
221 }
222
SizeOfType(u8 i_m3Type)223 u32 SizeOfType (u8 i_m3Type)
224 {
225 if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32)
226 return sizeof (i32);
227
228 return sizeof (i64);
229 }
230
231
232 //-- Binary Wasm parsing utils ------------------------------------------------------------------------------------------
233
234
Read_u64(u64 * o_value,bytes_t * io_bytes,cbytes_t i_end)235 M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
236 {
237 const u8 * ptr = * io_bytes;
238 ptr += sizeof (u64);
239
240 if (ptr <= i_end)
241 {
242 memcpy(o_value, * io_bytes, sizeof(u64));
243 M3_BSWAP_u64(*o_value);
244 * io_bytes = ptr;
245 return m3Err_none;
246 }
247 else return m3Err_wasmUnderrun;
248 }
249
250
Read_u32(u32 * o_value,bytes_t * io_bytes,cbytes_t i_end)251 M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
252 {
253 const u8 * ptr = * io_bytes;
254 ptr += sizeof (u32);
255
256 if (ptr <= i_end)
257 {
258 memcpy(o_value, * io_bytes, sizeof(u32));
259 M3_BSWAP_u32(*o_value);
260 * io_bytes = ptr;
261 return m3Err_none;
262 }
263 else return m3Err_wasmUnderrun;
264 }
265
266 #if d_m3ImplementFloat
267
Read_f64(f64 * o_value,bytes_t * io_bytes,cbytes_t i_end)268 M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
269 {
270 const u8 * ptr = * io_bytes;
271 ptr += sizeof (f64);
272
273 if (ptr <= i_end)
274 {
275 memcpy(o_value, * io_bytes, sizeof(f64));
276 M3_BSWAP_f64(*o_value);
277 * io_bytes = ptr;
278 return m3Err_none;
279 }
280 else return m3Err_wasmUnderrun;
281 }
282
283
Read_f32(f32 * o_value,bytes_t * io_bytes,cbytes_t i_end)284 M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
285 {
286 const u8 * ptr = * io_bytes;
287 ptr += sizeof (f32);
288
289 if (ptr <= i_end)
290 {
291 memcpy(o_value, * io_bytes, sizeof(f32));
292 M3_BSWAP_f32(*o_value);
293 * io_bytes = ptr;
294 return m3Err_none;
295 }
296 else return m3Err_wasmUnderrun;
297 }
298
299 #endif
300
Read_u8(u8 * o_value,bytes_t * io_bytes,cbytes_t i_end)301 M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
302 {
303 const u8 * ptr = * io_bytes;
304
305 if (ptr < i_end)
306 {
307 * o_value = * ptr;
308 * io_bytes = ptr + 1;
309
310 return m3Err_none;
311 }
312 else return m3Err_wasmUnderrun;
313 }
314
Read_opcode(m3opcode_t * o_value,bytes_t * io_bytes,cbytes_t i_end)315 M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end)
316 {
317 const u8 * ptr = * io_bytes;
318
319 if (ptr < i_end)
320 {
321 m3opcode_t opcode = * ptr++;
322
323 #ifndef d_m3EnableExtendedOpcodes
324 if (UNLIKELY(opcode == 0xFC))
325 {
326 if (ptr < i_end)
327 {
328 opcode = (opcode << 8) | (* ptr++);
329 }
330 else return m3Err_wasmUnderrun;
331 }
332 #endif
333 * o_value = opcode;
334 * io_bytes = ptr;
335
336 return m3Err_none;
337 }
338 else return m3Err_wasmUnderrun;
339 }
340
341
ReadLebUnsigned(u64 * o_value,u32 i_maxNumBits,bytes_t * io_bytes,cbytes_t i_end)342 M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end)
343 {
344 M3Result result = m3Err_wasmUnderrun;
345
346 u64 value = 0;
347
348 u32 shift = 0;
349 const u8 * ptr = * io_bytes;
350
351 while (ptr < i_end)
352 {
353 u64 byte = * (ptr++);
354
355 value |= ((byte & 0x7f) << shift);
356 shift += 7;
357
358 if ((byte & 0x80) == 0)
359 {
360 result = m3Err_none;
361 break;
362 }
363
364 if (shift >= i_maxNumBits)
365 {
366 result = m3Err_lebOverflow;
367 break;
368 }
369 }
370
371 * o_value = value;
372 * io_bytes = ptr;
373
374 return result;
375 }
376
377
ReadLebSigned(i64 * o_value,u32 i_maxNumBits,bytes_t * io_bytes,cbytes_t i_end)378 M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end)
379 {
380 M3Result result = m3Err_wasmUnderrun;
381
382 i64 value = 0;
383
384 u32 shift = 0;
385 const u8 * ptr = * io_bytes;
386
387 while (ptr < i_end)
388 {
389 u64 byte = * (ptr++);
390
391 value |= ((byte & 0x7f) << shift);
392 shift += 7;
393
394 if ((byte & 0x80) == 0)
395 {
396 result = m3Err_none;
397
398 if ((byte & 0x40) and (shift < 64)) // do sign extension
399 {
400 u64 extend = 0;
401 value |= (~extend << shift);
402 }
403
404 break;
405 }
406
407 if (shift >= i_maxNumBits)
408 {
409 result = m3Err_lebOverflow;
410 break;
411 }
412 }
413
414 * o_value = value;
415 * io_bytes = ptr;
416
417 return result;
418 }
419
420
ReadLEB_u32(u32 * o_value,bytes_t * io_bytes,cbytes_t i_end)421 M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
422 {
423 u64 value;
424 M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end);
425 * o_value = (u32) value;
426
427 return result;
428 }
429
430
ReadLEB_u7(u8 * o_value,bytes_t * io_bytes,cbytes_t i_end)431 M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
432 {
433 u64 value;
434 M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end);
435 * o_value = (u8) value;
436
437 return result;
438 }
439
440
ReadLEB_i7(i8 * o_value,bytes_t * io_bytes,cbytes_t i_end)441 M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
442 {
443 i64 value;
444 M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end);
445 * o_value = (i8) value;
446
447 return result;
448 }
449
450
ReadLEB_i32(i32 * o_value,bytes_t * io_bytes,cbytes_t i_end)451 M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
452 {
453 i64 value;
454 M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end);
455 * o_value = (i32) value;
456
457 return result;
458 }
459
460
ReadLEB_i64(i64 * o_value,bytes_t * io_bytes,cbytes_t i_end)461 M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
462 {
463 i64 value;
464 M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end);
465 * o_value = value;
466
467 return result;
468 }
469
470
Read_utf8(cstr_t * o_utf8,bytes_t * io_bytes,cbytes_t i_end)471 M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end)
472 {
473 *o_utf8 = NULL;
474
475 u32 utf8Length;
476 M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end);
477
478 if (not result)
479 {
480 if (utf8Length <= d_m3MaxSaneUtf8Length)
481 {
482 const u8 * ptr = * io_bytes;
483 const u8 * end = ptr + utf8Length;
484
485 if (end <= i_end)
486 {
487 char * utf8 = (char *)m3_Malloc (utf8Length + 1);
488
489 if (utf8)
490 {
491 memcpy (utf8, ptr, utf8Length);
492 utf8 [utf8Length] = 0;
493 * o_utf8 = utf8;
494 }
495
496 * io_bytes = end;
497 }
498 else result = m3Err_wasmUnderrun;
499 }
500 else result = m3Err_missingUTF8;
501 }
502
503 return result;
504 }
505
506 #if d_m3RecordBacktraces
FindModuleOffset(IM3Runtime i_runtime,pc_t i_pc)507 u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc)
508 {
509 // walk the code pages
510 IM3CodePage curr = i_runtime->pagesOpen;
511 bool pageFound = false;
512
513 while (curr)
514 {
515 if (ContainsPC (curr, i_pc))
516 {
517 pageFound = true;
518 break;
519 }
520 curr = curr->info.next;
521 }
522
523 if (!pageFound)
524 {
525 curr = i_runtime->pagesFull;
526 while (curr)
527 {
528 if (ContainsPC (curr, i_pc))
529 {
530 pageFound = true;
531 break;
532 }
533 curr = curr->info.next;
534 }
535 }
536
537 if (pageFound)
538 {
539 u32 result = 0;
540
541 bool pcFound = MapPCToOffset (curr, i_pc, & result);
542 d_m3Assert (pcFound);
543
544 return result;
545 }
546 else return 0;
547 }
548
549
PushBacktraceFrame(IM3Runtime io_runtime,pc_t i_pc)550 void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc)
551 {
552 // don't try to push any more frames if we've already had an alloc failure
553 if (UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED))
554 return;
555
556 M3BacktraceFrame * newFrame = m3_AllocStruct(M3BacktraceFrame);
557
558 if (!newFrame)
559 {
560 io_runtime->backtrace.lastFrame = M3_BACKTRACE_TRUNCATED;
561 return;
562 }
563
564 newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc);
565
566 if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame)
567 io_runtime->backtrace.frames = newFrame;
568 else
569 io_runtime->backtrace.lastFrame->next = newFrame;
570 io_runtime->backtrace.lastFrame = newFrame;
571 }
572
573
FillBacktraceFunctionInfo(IM3Runtime io_runtime,IM3Function i_function)574 void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function)
575 {
576 // If we've had an alloc failure then the last frame doesn't refer to the
577 // frame we want to fill in the function info for.
578 if (UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED))
579 return;
580
581 if (!io_runtime->backtrace.lastFrame)
582 return;
583
584 io_runtime->backtrace.lastFrame->function = i_function;
585 }
586
587
ClearBacktrace(IM3Runtime io_runtime)588 void ClearBacktrace (IM3Runtime io_runtime)
589 {
590 M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames;
591 while (currentFrame)
592 {
593 M3BacktraceFrame * nextFrame = currentFrame->next;
594 m3_Free (currentFrame);
595 currentFrame = nextFrame;
596 }
597
598 io_runtime->backtrace.frames = NULL;
599 io_runtime->backtrace.lastFrame = NULL;
600 }
601 #endif // d_m3RecordBacktraces
602