Coverage Report

Created: 2025-11-11 07:00

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
14.6k
{
129
14.6k
    return calloc (i_size, 1);
130
14.6k
}
131
132
void  m3_Free_Impl  (void * io_ptr)
133
27.7k
{
134
27.7k
    free (io_ptr);
135
27.7k
}
136
137
void *  m3_Realloc_Impl  (void * i_ptr, size_t i_newSize, size_t i_oldSize)
138
790
{
139
790
    if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr;
140
141
790
    void * newPtr = realloc (i_ptr, i_newSize);
142
143
790
    if (M3_LIKELY(newPtr))
144
790
    {
145
790
        if (i_newSize > i_oldSize) {
146
790
            memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize);
147
790
        }
148
790
        return newPtr;
149
790
    }
150
0
    return NULL;
151
790
}
152
153
#endif
154
155
void *  m3_CopyMem  (const void * i_from, size_t i_size)
156
3
{
157
3
    void * ptr = m3_Malloc("CopyMem", i_size);
158
3
    if (ptr) {
159
3
        memcpy (ptr, i_from, i_size);
160
3
    }
161
3
    return ptr;
162
3
}
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
13.0k
{
200
13.0k
    M3Result result = m3Err_none;
201
202
13.0k
    u8 type = -i_convolutedWasmType;
203
204
13.0k
    if (type == 0x40)
205
8.54k
        type = c_m3Type_none;
206
4.49k
    else if (type < c_m3Type_i32 or type > c_m3Type_f64)
207
6
        result = m3Err_invalidTypeId;
208
209
13.0k
    * o_type = type;
210
211
13.0k
    return result;
212
13.0k
}
213
214
215
bool  IsFpType  (u8 i_m3Type)
216
3.02k
{
217
3.02k
    return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64);
218
3.02k
}
219
220
221
bool  IsIntType  (u8 i_m3Type)
222
126
{
223
126
    return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64);
224
126
}
225
226
227
bool  Is64BitType  (u8 i_m3Type)
228
377k
{
229
377k
    if (i_m3Type == c_m3Type_i64 or i_m3Type == c_m3Type_f64)
230
107k
        return true;
231
269k
    else if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_none)
232
269k
        return false;
233
36
    else
234
36
        return (sizeof (voidptr_t) == 8); // all other cases are pointers
235
377k
}
236
237
u32  SizeOfType  (u8 i_m3Type)
238
124
{
239
124
    if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32)
240
5
        return sizeof (i32);
241
242
119
    return sizeof (i64);
243
124
}
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
398
{
267
398
    const u8 * ptr = * io_bytes;
268
398
    ptr += sizeof (u32);
269
270
398
    if (ptr <= i_end)
271
398
    {
272
398
        memcpy(o_value, * io_bytes, sizeof(u32));
273
398
        M3_BSWAP_u32(*o_value);
274
398
        * io_bytes = ptr;
275
398
        return m3Err_none;
276
398
    }
277
0
    else return m3Err_wasmUnderrun;
278
398
}
279
280
#if d_m3ImplementFloat
281
282
M3Result  Read_f64  (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
283
355
{
284
355
    const u8 * ptr = * io_bytes;
285
355
    ptr += sizeof (f64);
286
287
355
    if (ptr <= i_end)
288
355
    {
289
355
        memcpy(o_value, * io_bytes, sizeof(f64));
290
355
        M3_BSWAP_f64(*o_value);
291
355
        * io_bytes = ptr;
292
355
        return m3Err_none;
293
355
    }
294
0
    else return m3Err_wasmUnderrun;
295
355
}
296
297
298
M3Result  Read_f32  (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
299
461
{
300
461
    const u8 * ptr = * io_bytes;
301
461
    ptr += sizeof (f32);
302
303
461
    if (ptr <= i_end)
304
461
    {
305
461
        memcpy(o_value, * io_bytes, sizeof(f32));
306
461
        M3_BSWAP_f32(*o_value);
307
461
        * io_bytes = ptr;
308
461
        return m3Err_none;
309
461
    }
310
0
    else return m3Err_wasmUnderrun;
311
461
}
312
313
#endif
314
315
M3Result  Read_u8  (u8 * o_value, bytes_t  * io_bytes, cbytes_t i_end)
316
5.66k
{
317
5.66k
    const u8 * ptr = * io_bytes;
318
319
5.66k
    if (ptr < i_end)
320
5.66k
    {
321
5.66k
        * o_value = * ptr;
322
5.66k
        * io_bytes = ptr + 1;
323
324
5.66k
        return m3Err_none;
325
5.66k
    }
326
0
    else return m3Err_wasmUnderrun;
327
5.66k
}
328
329
M3Result  Read_opcode  (m3opcode_t * o_value, bytes_t  * io_bytes, cbytes_t i_end)
330
36.2k
{
331
36.2k
    const u8 * ptr = * io_bytes;
332
333
36.2k
    if (ptr < i_end)
334
36.2k
    {
335
36.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
36.2k
        * o_value = opcode;
348
36.2k
        * io_bytes = ptr;
349
350
36.2k
        return m3Err_none;
351
36.2k
    }
352
0
    else return m3Err_wasmUnderrun;
353
36.2k
}
354
355
356
M3Result  ReadLebUnsigned  (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end)
357
30.5k
{
358
30.5k
    M3Result result = m3Err_wasmUnderrun;
359
360
30.5k
    u64 value = 0;
361
362
30.5k
    u32 shift = 0;
363
30.5k
    const u8 * ptr = * io_bytes;
364
365
33.9k
    while (ptr < i_end)
366
33.9k
    {
367
33.9k
        u64 byte = * (ptr++);
368
369
33.9k
        value |= ((byte & 0x7f) << shift);
370
33.9k
        shift += 7;
371
372
33.9k
        if ((byte & 0x80) == 0)
373
30.5k
        {
374
30.5k
            result = m3Err_none;
375
30.5k
            break;
376
30.5k
        }
377
378
3.36k
        if (shift >= i_maxNumBits)
379
6
        {
380
6
            result = m3Err_lebOverflow;
381
6
            break;
382
6
        }
383
3.36k
    }
384
385
30.5k
    * o_value = value;
386
30.5k
    * io_bytes = ptr;
387
388
30.5k
    return result;
389
30.5k
}
390
391
392
M3Result  ReadLebSigned  (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end)
393
46.1k
{
394
46.1k
    M3Result result = m3Err_wasmUnderrun;
395
396
46.1k
    i64 value = 0;
397
398
46.1k
    u32 shift = 0;
399
46.1k
    const u8 * ptr = * io_bytes;
400
401
103k
    while (ptr < i_end)
402
103k
    {
403
103k
        u64 byte = * (ptr++);
404
405
103k
        value |= ((byte & 0x7f) << shift);
406
103k
        shift += 7;
407
408
103k
        if ((byte & 0x80) == 0)
409
46.1k
        {
410
46.1k
            result = m3Err_none;
411
412
46.1k
            if ((byte & 0x40) and (shift < 64))    // do sign extension
413
40.5k
            {
414
40.5k
                u64 extend = 0;
415
40.5k
                value |= (~extend << shift);
416
40.5k
            }
417
418
46.1k
            break;
419
46.1k
        }
420
421
57.1k
        if (shift >= i_maxNumBits)
422
2
        {
423
2
            result = m3Err_lebOverflow;
424
2
            break;
425
2
        }
426
57.1k
    }
427
428
46.1k
    * o_value = value;
429
46.1k
    * io_bytes = ptr;
430
431
46.1k
    return result;
432
46.1k
}
433
434
435
M3Result  ReadLEB_u32  (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
436
25.7k
{
437
25.7k
    u64 value;
438
25.7k
    M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end);
439
25.7k
    * o_value = (u32) value;
440
441
25.7k
    return result;
442
25.7k
}
443
444
445
M3Result  ReadLEB_u7  (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
446
4.83k
{
447
4.83k
    u64 value;
448
4.83k
    M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end);
449
4.83k
    * o_value = (u8) value;
450
451
4.83k
    return result;
452
4.83k
}
453
454
455
M3Result  ReadLEB_i7  (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end)
456
13.2k
{
457
13.2k
    i64 value;
458
13.2k
    M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end);
459
13.2k
    * o_value = (i8) value;
460
461
13.2k
    return result;
462
13.2k
}
463
464
465
M3Result  ReadLEB_i32  (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end)
466
18.6k
{
467
18.6k
    i64 value;
468
18.6k
    M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end);
469
18.6k
    * o_value = (i32) value;
470
471
18.6k
    return result;
472
18.6k
}
473
474
475
M3Result  ReadLEB_i64  (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end)
476
13.8k
{
477
13.8k
    i64 value;
478
13.8k
    M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end);
479
13.8k
    * o_value = value;
480
481
13.8k
    return result;
482
13.8k
}
483
484
485
M3Result  Read_utf8  (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end)
486
12.4k
{
487
12.4k
    *o_utf8 = NULL;
488
489
12.4k
    u32 utf8Length;
490
12.4k
    M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end);
491
492
12.4k
    if (not result)
493
12.4k
    {
494
12.4k
        if (utf8Length <= d_m3MaxSaneUtf8Length)
495
12.4k
        {
496
12.4k
            const u8 * ptr = * io_bytes;
497
12.4k
            const u8 * end = ptr + utf8Length;
498
499
12.4k
            if (end <= i_end)
500
12.4k
            {
501
12.4k
                char * utf8 = (char *)m3_Malloc ("UTF8", utf8Length + 1);
502
503
12.4k
                if (utf8)
504
12.4k
                {
505
12.4k
                    memcpy (utf8, ptr, utf8Length);
506
12.4k
                    utf8 [utf8Length] = 0;
507
12.4k
                    * o_utf8 = utf8;
508
12.4k
                }
509
510
12.4k
                * io_bytes = end;
511
12.4k
            }
512
8
            else result = m3Err_wasmUnderrun;
513
12.4k
        }
514
7
        else result = m3Err_missingUTF8;
515
12.4k
    }
516
517
12.4k
    return result;
518
12.4k
}
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