// // m3_core.c // // Created by Steven Massey on 4/15/19. // Copyright © 2019 Steven Massey. All rights reserved. // #define M3_IMPLEMENT_ERROR_STRINGS #include "m3_config.h" #include "wasm3.h" #include "m3_core.h" #include "m3_env.h" void m3_Abort(const char* message) { #ifdef DEBUG fprintf(stderr, "Error: %s\n", message); #endif abort(); } M3_WEAK M3Result m3_Yield () { return m3Err_none; } #if d_m3LogTimestamps #include #define SEC_TO_US(sec) ((sec)*1000000) #define NS_TO_US(ns) ((ns)/1000) static uint64_t initial_ts = -1; uint64_t m3_GetTimestamp() { if (initial_ts == -1) { initial_ts = 0; initial_ts = m3_GetTimestamp(); } struct timespec ts; timespec_get(&ts, TIME_UTC); uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec); return us - initial_ts; } #endif #if d_m3FixedHeap static u8 fixedHeap[d_m3FixedHeap]; static u8* fixedHeapPtr = fixedHeap; static u8* const fixedHeapEnd = fixedHeap + d_m3FixedHeap; static u8* fixedHeapLast = NULL; #if d_m3FixedHeapAlign > 1 # define HEAP_ALIGN_PTR(P) P = (u8*)(((size_t)(P)+(d_m3FixedHeapAlign-1)) & ~ (d_m3FixedHeapAlign-1)); #else # define HEAP_ALIGN_PTR(P) #endif void * m3_Malloc_Impl (size_t i_size) { u8 * ptr = fixedHeapPtr; fixedHeapPtr += i_size; HEAP_ALIGN_PTR(fixedHeapPtr); if (fixedHeapPtr >= fixedHeapEnd) { return NULL; } memset (ptr, 0x0, i_size); fixedHeapLast = ptr; return ptr; } void m3_Free_Impl (void * i_ptr) { // Handle the last chunk if (i_ptr && i_ptr == fixedHeapLast) { fixedHeapPtr = fixedHeapLast; fixedHeapLast = NULL; } else { //printf("== free %p [failed]\n", io_ptr); } } void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) { if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; void * newPtr; // Handle the last chunk if (i_ptr && i_ptr == fixedHeapLast) { fixedHeapPtr = fixedHeapLast + i_newSize; HEAP_ALIGN_PTR(fixedHeapPtr); if (fixedHeapPtr >= fixedHeapEnd) { return NULL; } newPtr = i_ptr; } else { newPtr = m3_Malloc_Impl(i_newSize); if (!newPtr) { return NULL; } if (i_ptr) { memcpy(newPtr, i_ptr, i_oldSize); } } if (i_newSize > i_oldSize) { memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); } return newPtr; } #else void * m3_Malloc_Impl (size_t i_size) { return calloc (i_size, 1); } void m3_Free_Impl (void * io_ptr) { free (io_ptr); } void * m3_Realloc_Impl (void * i_ptr, size_t i_newSize, size_t i_oldSize) { if (M3_UNLIKELY(i_newSize == i_oldSize)) return i_ptr; void * newPtr = realloc (i_ptr, i_newSize); if (M3_LIKELY(newPtr)) { if (i_newSize > i_oldSize) { memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); } return newPtr; } return NULL; } #endif void * m3_CopyMem (const void * i_from, size_t i_size) { void * ptr = m3_Malloc("CopyMem", i_size); if (ptr) { memcpy (ptr, i_from, i_size); } return ptr; } //-------------------------------------------------------------------------------------------- #if d_m3LogNativeStack static size_t stack_start; static size_t stack_end; void m3StackCheckInit () { char stack; stack_end = stack_start = (size_t)&stack; } void m3StackCheck () { char stack; size_t addr = (size_t)&stack; size_t stackEnd = stack_end; stack_end = M3_MIN (stack_end, addr); // if (stackEnd != stack_end) // printf ("maxStack: %ld\n", m3StackGetMax ()); } int m3StackGetMax () { return stack_start - stack_end; } #endif //-------------------------------------------------------------------------------------------- M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType) { M3Result result = m3Err_none; u8 type = -i_convolutedWasmType; if (type == 0x40) type = c_m3Type_none; else if (type < c_m3Type_i32 or type > c_m3Type_f64) result = m3Err_invalidTypeId; * o_type = type; return result; } bool IsFpType (u8 i_m3Type) { return (i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_f64); } bool IsIntType (u8 i_m3Type) { return (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_i64); } bool Is64BitType (u8 i_m3Type) { if (i_m3Type == c_m3Type_i64 or i_m3Type == c_m3Type_f64) return true; else if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32 or i_m3Type == c_m3Type_none) return false; else return (sizeof (voidptr_t) == 8); // all other cases are pointers } u32 SizeOfType (u8 i_m3Type) { if (i_m3Type == c_m3Type_i32 or i_m3Type == c_m3Type_f32) return sizeof (i32); return sizeof (i64); } //-- Binary Wasm parsing utils ------------------------------------------------------------------------------------------ M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end) { const u8 * ptr = * io_bytes; ptr += sizeof (u64); if (ptr <= i_end) { memcpy(o_value, * io_bytes, sizeof(u64)); M3_BSWAP_u64(*o_value); * io_bytes = ptr; return m3Err_none; } else return m3Err_wasmUnderrun; } M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) { const u8 * ptr = * io_bytes; ptr += sizeof (u32); if (ptr <= i_end) { memcpy(o_value, * io_bytes, sizeof(u32)); M3_BSWAP_u32(*o_value); * io_bytes = ptr; return m3Err_none; } else return m3Err_wasmUnderrun; } #if d_m3ImplementFloat M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end) { const u8 * ptr = * io_bytes; ptr += sizeof (f64); if (ptr <= i_end) { memcpy(o_value, * io_bytes, sizeof(f64)); M3_BSWAP_f64(*o_value); * io_bytes = ptr; return m3Err_none; } else return m3Err_wasmUnderrun; } M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end) { const u8 * ptr = * io_bytes; ptr += sizeof (f32); if (ptr <= i_end) { memcpy(o_value, * io_bytes, sizeof(f32)); M3_BSWAP_f32(*o_value); * io_bytes = ptr; return m3Err_none; } else return m3Err_wasmUnderrun; } #endif M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) { const u8 * ptr = * io_bytes; if (ptr < i_end) { * o_value = * ptr; * io_bytes = ptr + 1; return m3Err_none; } else return m3Err_wasmUnderrun; } M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end) { const u8 * ptr = * io_bytes; if (ptr < i_end) { m3opcode_t opcode = * ptr++; #if d_m3CascadedOpcodes == 0 if (M3_UNLIKELY(opcode == c_waOp_extended)) { if (ptr < i_end) { opcode = (opcode << 8) | (* ptr++); } else return m3Err_wasmUnderrun; } #endif * o_value = opcode; * io_bytes = ptr; return m3Err_none; } else return m3Err_wasmUnderrun; } M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) { M3Result result = m3Err_wasmUnderrun; u64 value = 0; u32 shift = 0; const u8 * ptr = * io_bytes; while (ptr < i_end) { u64 byte = * (ptr++); value |= ((byte & 0x7f) << shift); shift += 7; if ((byte & 0x80) == 0) { result = m3Err_none; break; } if (shift >= i_maxNumBits) { result = m3Err_lebOverflow; break; } } * o_value = value; * io_bytes = ptr; return result; } M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end) { M3Result result = m3Err_wasmUnderrun; i64 value = 0; u32 shift = 0; const u8 * ptr = * io_bytes; while (ptr < i_end) { u64 byte = * (ptr++); value |= ((byte & 0x7f) << shift); shift += 7; if ((byte & 0x80) == 0) { result = m3Err_none; if ((byte & 0x40) and (shift < 64)) // do sign extension { u64 extend = 0; value |= (~extend << shift); } break; } if (shift >= i_maxNumBits) { result = m3Err_lebOverflow; break; } } * o_value = value; * io_bytes = ptr; return result; } M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) { u64 value; M3Result result = ReadLebUnsigned (& value, 32, io_bytes, i_end); * o_value = (u32) value; return result; } M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) { u64 value; M3Result result = ReadLebUnsigned (& value, 7, io_bytes, i_end); * o_value = (u8) value; return result; } M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end) { i64 value; M3Result result = ReadLebSigned (& value, 7, io_bytes, i_end); * o_value = (i8) value; return result; } M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end) { i64 value; M3Result result = ReadLebSigned (& value, 32, io_bytes, i_end); * o_value = (i32) value; return result; } M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end) { i64 value; M3Result result = ReadLebSigned (& value, 64, io_bytes, i_end); * o_value = value; return result; } M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) { *o_utf8 = NULL; u32 utf8Length; M3Result result = ReadLEB_u32 (& utf8Length, io_bytes, i_end); if (not result) { if (utf8Length <= d_m3MaxSaneUtf8Length) { const u8 * ptr = * io_bytes; const u8 * end = ptr + utf8Length; if (end <= i_end) { char * utf8 = (char *)m3_Malloc ("UTF8", utf8Length + 1); if (utf8) { memcpy (utf8, ptr, utf8Length); utf8 [utf8Length] = 0; * o_utf8 = utf8; } * io_bytes = end; } else result = m3Err_wasmUnderrun; } else result = m3Err_missingUTF8; } return result; } #if d_m3RecordBacktraces u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc) { // walk the code pages IM3CodePage curr = i_runtime->pagesOpen; bool pageFound = false; while (curr) { if (ContainsPC (curr, i_pc)) { pageFound = true; break; } curr = curr->info.next; } if (!pageFound) { curr = i_runtime->pagesFull; while (curr) { if (ContainsPC (curr, i_pc)) { pageFound = true; break; } curr = curr->info.next; } } if (pageFound) { u32 result = 0; bool pcFound = MapPCToOffset (curr, i_pc, & result); d_m3Assert (pcFound); return result; } else return 0; } void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) { // don't try to push any more frames if we've already had an alloc failure if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) return; M3BacktraceFrame * newFrame = m3_AllocStruct(M3BacktraceFrame); if (!newFrame) { io_runtime->backtrace.lastFrame = M3_BACKTRACE_TRUNCATED; return; } newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc); if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame) io_runtime->backtrace.frames = newFrame; else io_runtime->backtrace.lastFrame->next = newFrame; io_runtime->backtrace.lastFrame = newFrame; } void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function) { // If we've had an alloc failure then the last frame doesn't refer to the // frame we want to fill in the function info for. if (M3_UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) return; if (!io_runtime->backtrace.lastFrame) return; io_runtime->backtrace.lastFrame->function = i_function; } void ClearBacktrace (IM3Runtime io_runtime) { M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames; while (currentFrame) { M3BacktraceFrame * nextFrame = currentFrame->next; m3_Free (currentFrame); currentFrame = nextFrame; } io_runtime->backtrace.frames = NULL; io_runtime->backtrace.lastFrame = NULL; } #endif // d_m3RecordBacktraces