Coverage Report

Created: 2026-05-30 06:28

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