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