Coverage Report

Created: 2025-10-10 06:05

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