diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 7286f655004ca9..684a23b6f1b0b3 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -39,6 +39,11 @@ if (DEFINED CLR_CMAKE_ICU_DIR) include_directories(${CLR_CMAKE_ICU_DIR}/include) endif(DEFINED CLR_CMAKE_ICU_DIR) +if (CLR_CMAKE_TARGET_ARCH_WASM) + add_compile_options(-fwasm-exceptions) + add_link_options(-fwasm-exceptions -sEXIT_RUNTIME=1 -sUSE_OFFSET_CONVERTER=1) +endif() + #---------------------------------------------------- # Cross target Component build specific configuration #---------------------------------------------------- diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs index df3da7a876b787..5bafebd4cb140d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs @@ -51,9 +51,9 @@ class AsmOffsets public const int OFFSETOF__REGDISPLAY__SP = 0xba8; public const int OFFSETOF__REGDISPLAY__ControlPC = 0xbb0; #elif TARGET_WASM - public const int SIZEOF__REGDISPLAY = 0x3c; - public const int OFFSETOF__REGDISPLAY__SP = 0x34; - public const int OFFSETOF__REGDISPLAY__ControlPC = 0x38; + public const int SIZEOF__REGDISPLAY = 0x38; + public const int OFFSETOF__REGDISPLAY__SP = 0x30; + public const int OFFSETOF__REGDISPLAY__ControlPC = 0x34; #endif #if TARGET_64BIT @@ -73,9 +73,14 @@ class AsmOffsets public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x3d0; #else // TARGET_64BIT public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x4; +#if FEATURE_INTERPRETER + public const int SIZEOF__StackFrameIterator = 0xdc; + public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xd8; +#else public const int SIZEOF__StackFrameIterator = 0xcc; - public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0xba; public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0xc8; +#endif + public const int OFFSETOF__StackFrameIterator__m_isRuntimeWrappedExceptions = 0xba; #endif // TARGET_64BIT #else // DEBUG @@ -167,7 +172,7 @@ class AsmOffsets #elif TARGET_LOONGARCH64 public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x520; #elif TARGET_WASM - public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x08; + public const int SIZEOF__PAL_LIMITED_CONTEXT = 0x04; #endif #if TARGET_AMD64 diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 133d30d41e615d..58d21f418f712f 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -35,7 +35,9 @@ if(CLR_CMAKE_TARGET_LINUX_MUSL) add_definitions(-DNO_FIXED_STACK_LIMIT) endif(CLR_CMAKE_TARGET_LINUX_MUSL) -add_definitions(-DDEBUGGING_SUPPORTED) +#if(NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_definitions(-DDEBUGGING_SUPPORTED) +#endif() add_compile_definitions($<$>>:PROFILING_SUPPORTED>) add_compile_definitions($<$>:PROFILING_SUPPORTED_DATA>) @@ -270,4 +272,5 @@ function(set_target_definitions_to_custom_os_and_arch) if (TARGETDETAILS_ARCH STREQUAL "armel") target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE ARM_SOFTFP) endif() + endfunction() diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index 93a32c3df576f5..f7ee1864daa8c4 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -31,11 +31,11 @@ if(NOT DEFINED FEATURE_DBGIPC) endif(NOT DEFINED FEATURE_DBGIPC) if(NOT DEFINED FEATURE_INTERPRETER) - if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) + if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_WASM) set(FEATURE_INTERPRETER $,1,0>) - else(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) + else(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_WASM) set(FEATURE_INTERPRETER 0) - endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) + endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_WASM) endif(NOT DEFINED FEATURE_INTERPRETER) if(NOT DEFINED FEATURE_STANDALONE_GC) @@ -50,7 +50,7 @@ if(NOT DEFINED FEATURE_SINGLE_FILE_DIAGNOSTICS) set(FEATURE_SINGLE_FILE_DIAGNOSTICS 1) endif(NOT DEFINED FEATURE_SINGLE_FILE_DIAGNOSTICS) -if (CLR_CMAKE_TARGET_WIN32 OR CLR_CMAKE_TARGET_UNIX) +if ((CLR_CMAKE_TARGET_WIN32 OR CLR_CMAKE_TARGET_UNIX) AND NOT CLR_CMAKE_TARGET_ARCH_WASM) set(FEATURE_COMWRAPPERS 1) endif() diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index 60f6a310451d90..5b5e471718f101 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -56,8 +56,14 @@ endif (CLR_CMAKE_HOST_WIN32) add_definitions(-DFX_VER_INTERNALNAME_STR=CoreCLR.dll) +if (CLR_CMAKE_TARGET_ARCH_WASM) + set(LINK_TYPE STATIC) +else() + set(LINK_TYPE SHARED) +endif() + add_library_clr(coreclr - SHARED + ${LINK_TYPE} ${CLR_SOURCES} ) @@ -70,9 +76,15 @@ add_dependencies(coreclr coreclr_exports) set_property(TARGET coreclr APPEND_STRING PROPERTY LINK_FLAGS ${EXPORTS_LINKER_OPTION}) set_property(TARGET coreclr APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE}) -if (CLR_CMAKE_HOST_UNIX) +if (CLR_CMAKE_HOST_UNIX AND NOT CLR_CMAKE_TARGET_ARCH_WASM) set(LIB_UNWINDER unwinder_wks) -endif (CLR_CMAKE_HOST_UNIX) +endif (CLR_CMAKE_HOST_UNIX AND NOT CLR_CMAKE_TARGET_ARCH_WASM) + +if (NOT CLR_CMAKE_TARGET_ARCH_WASM) + set(LIB_CORDBEE cordbee_wks) + set(LIB_INTEROP interop) + set(LIB_CDAC_CONTRACT_DESCRIPTOR cdac_contract_descriptor) +endif (NOT CLR_CMAKE_TARGET_ARCH_WASM) # IMPORTANT! Please do not rearrange the order of the libraries. The linker on Linux is # order dependent and changing the order can result in undefined symbols in the shared @@ -80,7 +92,7 @@ endif (CLR_CMAKE_HOST_UNIX) set(CORECLR_LIBRARIES utilcode ${START_LIBRARY_GROUP} # Start group of libraries that have circular references - cordbee_wks + ${LIB_CORDBEE} debug-pal ${LIB_UNWINDER} v3binder @@ -95,10 +107,10 @@ set(CORECLR_LIBRARIES utilcode v3binder System.Globalization.Native-Static - interop + ${LIB_INTEROP} coreclrminipal gc_pal - cdac_contract_descriptor + ${LIB_CDAC_CONTRACT_DESCRIPTOR} ) if(CLR_CMAKE_TARGET_ARCH_AMD64) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 720828b2565f85..cb9b609721dbb1 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -244,6 +244,7 @@ bool GCToOSInterface::Initialize() // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize assert((((size_t)g_helperPage) & (OS_PAGE_SIZE - 1)) == 0); +#ifndef TARGET_BROWSER // Locking the page ensures that it stays in memory during the two mprotect // calls in the FlushProcessWriteBuffers below. If the page was unmapped between // those calls, they would not have the expected effect of generating IPI. @@ -253,6 +254,9 @@ bool GCToOSInterface::Initialize() { return false; } +#else + int status; +#endif // !TARGET_BROWSER status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL); if (status != 0) @@ -576,7 +580,7 @@ static void* VirtualReserveInner(size_t size, size_t alignment, uint32_t flags, } pRetVal = pAlignedRetVal; -#ifdef MADV_DONTDUMP +#if defined(MADV_DONTDUMP) && !defined(TARGET_BROWSER) // Do not include reserved uncommitted memory in coredump. if (!committing) { @@ -624,9 +628,13 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size) // true if it has succeeded, false if it has failed static bool VirtualCommitInner(void* address, size_t size, uint16_t node, bool newMemory) { +#ifndef TARGET_BROWSER bool success = mprotect(address, size, PROT_WRITE | PROT_READ) == 0; +#else + bool success = true; +#endif // !TARGET_BROWSER -#ifdef MADV_DODUMP +#if defined(MADV_DONTDUMP) && !defined(TARGET_BROWSER) if (success && !newMemory) { // Include committed memory in coredump. New memory is included by default. diff --git a/src/coreclr/hosts/CMakeLists.txt b/src/coreclr/hosts/CMakeLists.txt index dba881f7c6a3a7..4be3bbe8b95805 100644 --- a/src/coreclr/hosts/CMakeLists.txt +++ b/src/coreclr/hosts/CMakeLists.txt @@ -1,7 +1,11 @@ include_directories(inc) -if(CLR_CMAKE_HOST_WIN32) - add_subdirectory(coreshim) -endif(CLR_CMAKE_HOST_WIN32) +if (CLR_CMAKE_TARGET_ARCH_WASM) + add_subdirectory(corewasmrun) +else() + if(CLR_CMAKE_HOST_WIN32) + add_subdirectory(coreshim) + endif(CLR_CMAKE_HOST_WIN32) -add_subdirectory(corerun) + add_subdirectory(corerun) +endif() diff --git a/src/coreclr/hosts/corewasmrun/CMakeLists.txt b/src/coreclr/hosts/corewasmrun/CMakeLists.txt new file mode 100644 index 00000000000000..57eb957debfac3 --- /dev/null +++ b/src/coreclr/hosts/corewasmrun/CMakeLists.txt @@ -0,0 +1,28 @@ +project(corewasmrun) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +if (DEFINED CLR_CMAKE_ICU_DIR) + link_directories(${CLR_CMAKE_ICU_DIR}/lib) +endif(DEFINED CLR_CMAKE_ICU_DIR) + +add_executable_clr(corewasmrun + corewasmrun.cpp +) + +set(_WASM_PRELOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/../../../../../bin/coreclr/browser.wasm.Debug/IL") +if (EXISTS "${_WASM_PRELOAD_DIR}") + set(_WASM_PRELOAD_FILE --preload-file ${_WASM_PRELOAD_DIR}@/) +endif (EXISTS "${_WASM_PRELOAD_DIR}") + +target_compile_options(corewasmrun PRIVATE -fwasm-exceptions) +target_link_options(corewasmrun PRIVATE -fwasm-exceptions -sEXIT_RUNTIME=1 -sUSE_OFFSET_CONVERTER=1 -sINITIAL_MEMORY=134217728 -sFORCE_FILESYSTEM=1 ${_WASM_PRELOAD_FILE} -Wl,-error-limit=0) + +target_link_libraries(corewasmrun PRIVATE coreclr) +target_link_libraries(corewasmrun PRIVATE clrinterpreter) + +target_link_libraries(corewasmrun PRIVATE icuuc) +target_link_libraries(corewasmrun PRIVATE icui18n) +target_link_libraries(corewasmrun PRIVATE icudata) + +install_clr(TARGETS corewasmrun DESTINATIONS . COMPONENT hosts) diff --git a/src/coreclr/hosts/corewasmrun/corewasmrun.cpp b/src/coreclr/hosts/corewasmrun/corewasmrun.cpp new file mode 100644 index 00000000000000..dda735b72b521e --- /dev/null +++ b/src/coreclr/hosts/corewasmrun/corewasmrun.cpp @@ -0,0 +1,45 @@ +#include +#include + +static void log_error_info(const char* line) +{ + std::fprintf(stderr, "log error: %s\n", line); +} + +// The current CoreCLR instance details. +static void* CurrentClrInstance; +static unsigned int CurrentAppDomainId; + +static int run() +{ + const char* exe_path = ""; + const char* app_domain_name = "corewasmrun"; + const char* entry_assembly = "ManagedAssembly.dll"; + + coreclr_set_error_writer(log_error_info); + + printf("call coreclr_initialize\n"); + int retval = coreclr_initialize(exe_path, app_domain_name, 0, nullptr, nullptr, &CurrentClrInstance, &CurrentAppDomainId); + + if (retval < 0) + { + std::fprintf(stderr, "coreclr_initialize failed - Error: 0x%08x\n", retval); + return -1; + } + else + { + printf("coreclr_initialize succeeded - retval: 0x%08x\n", retval); + } + + // coreclr_execute_assembly(); + // coreclr_shutdown(); + + return retval; +} + +int main() +{ + int retval = run(); + + return retval; +} diff --git a/src/coreclr/hosts/corewasmrun/index.html b/src/coreclr/hosts/corewasmrun/index.html new file mode 100644 index 00000000000000..7fecf3afe85302 --- /dev/null +++ b/src/coreclr/hosts/corewasmrun/index.html @@ -0,0 +1,52 @@ + + + + + corewasmrun + + +

corewasmrun

+

+    
+    
+
diff --git a/src/coreclr/inc/clrnt.h b/src/coreclr/inc/clrnt.h
index 8040117a28b8f6..30cb7eacdff957 100644
--- a/src/coreclr/inc/clrnt.h
+++ b/src/coreclr/inc/clrnt.h
@@ -510,7 +510,7 @@ RtlVirtualUnwind(
 #define UNW_FLAG_EHANDLER               0x1             /* filter handler */
 #define UNW_FLAG_UHANDLER               0x2             /* unwind handler */
 
-PEXCEPTION_ROUTINE
+PEXCEPTION_ROUTINE inline
 RtlVirtualUnwind (
     _In_ DWORD HandlerType,
     _In_ DWORD ImageBase,
@@ -520,7 +520,11 @@ RtlVirtualUnwind (
     _Out_ PVOID *HandlerData,
     _Out_ PDWORD EstablisherFrame,
     __inout_opt PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers
-    );
+    )
+{
+    _ASSERTE("The function RtlVirtualUnwind is not implemented on wasm");
+    return nullptr;
+}
 
 FORCEINLINE
 ULONG
diff --git a/src/coreclr/inc/loaderheap.h b/src/coreclr/inc/loaderheap.h
index d3040e0b4aa448..49e9e4727bcba9 100644
--- a/src/coreclr/inc/loaderheap.h
+++ b/src/coreclr/inc/loaderheap.h
@@ -158,7 +158,7 @@ struct LoaderHeapEvent;
 // When an interleaved LoaderHeap is constructed, this is the interleaving size
 inline UINT32 GetStubCodePageSize()
 {
-#if defined(TARGET_ARM64) && defined(TARGET_UNIX)
+#if (defined(TARGET_ARM64) && defined(TARGET_UNIX)) || defined(TARGET_WASM)
     return max(16*1024u, GetOsPageSize());
 #elif defined(TARGET_ARM)
     return 4096; // ARM is special as the 32bit instruction set does not easily permit a 16KB offset
diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h
index 9ee206edd34098..4380d1a08c31a5 100644
--- a/src/coreclr/interpreter/compiler.h
+++ b/src/coreclr/interpreter/compiler.h
@@ -321,8 +321,13 @@ class InterpCompiler
     CORINFO_METHOD_INFO* m_methodInfo;
 #ifdef DEBUG
     const char *m_methodName;
+#ifdef __wasm__
+    // enable verbose output on wasm temporarily
+    bool m_verbose = true;
+#else
     bool m_verbose = false;
-#endif
+#endif // __wasm__
+#endif // DEBUG
 
     static int32_t InterpGetMovForType(InterpType interpType, bool signExtend);
 
diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp
index 4a2516bea58484..61359029418ce2 100644
--- a/src/coreclr/minipal/Unix/doublemapping.cpp
+++ b/src/coreclr/minipal/Unix/doublemapping.cpp
@@ -47,6 +47,9 @@ static const off_t MaxDoubleMappedSize = UINT_MAX;
 
 bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize)
 {
+#ifdef __wasm__
+    return false;
+#endif
     if (minipal_detect_rosetta())
     {
         // Rosetta doesn't support double mapping correctly
diff --git a/src/coreclr/pal/inc/rt/palrt.h b/src/coreclr/pal/inc/rt/palrt.h
index 215290ce093439..67697a63e656e5 100644
--- a/src/coreclr/pal/inc/rt/palrt.h
+++ b/src/coreclr/pal/inc/rt/palrt.h
@@ -1008,7 +1008,11 @@ typedef struct _DISPATCHER_CONTEXT {
 typedef struct _DISPATCHER_CONTEXT {
     // WASM does not build the VM or JIT at this point,
     // so we only provide a dummy definition.
-    DWORD Reserved;
+    UINT32 ControlPc;
+    UINT32 ImageBase;
+    PRUNTIME_FUNCTION FunctionEntry;
+    UINT32 EstablisherFrame;
+    PCONTEXT ContextRecord;
 } DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;
 
 #else
diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp
index 444bad7e5c072c..b0501bc993f5cb 100644
--- a/src/coreclr/pal/src/exception/signal.cpp
+++ b/src/coreclr/pal/src/exception/signal.cpp
@@ -219,6 +219,7 @@ BOOL SEHInitializeSignals(CorUnix::CPalThread *pthrCurrent, DWORD flags)
             return FALSE;
         }
 
+#ifndef TARGET_BROWSER
         // create a guard page for the alternate stack
         int st = mprotect((void*)g_stackOverflowHandlerStack, GetVirtualPageSize(), PROT_NONE);
         if (st != 0)
@@ -226,6 +227,7 @@ BOOL SEHInitializeSignals(CorUnix::CPalThread *pthrCurrent, DWORD flags)
             munmap((void*)g_stackOverflowHandlerStack, stackOverflowStackSize);
             return FALSE;
         }
+#endif // TARGET_BROWSER
 
         g_stackOverflowHandlerStack = (void*)((size_t)g_stackOverflowHandlerStack + stackOverflowStackSize);
 #endif // HAVE_MACH_EXCEPTIONS
diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp
index 6811690132daed..aa0e1a5592ba4b 100644
--- a/src/coreclr/pal/src/init/pal.cpp
+++ b/src/coreclr/pal/src/init/pal.cpp
@@ -719,6 +719,7 @@ PAL_InitializeCoreCLR(const char *szExePath, BOOL runningInExe)
         return ERROR_SUCCESS;
     }
 
+#ifndef __wasm__ // we don't use shared libraries on wasm
     // Now that the PAL is initialized it's safe to call the initialization methods for the code that used to
     // be dynamically loaded libraries but is now statically linked into CoreCLR just like the PAL, i.e. the
     // PAL RT and mscorwks.
@@ -726,6 +727,7 @@ PAL_InitializeCoreCLR(const char *szExePath, BOOL runningInExe)
     {
         return ERROR_DLL_INIT_FAILED;
     }
+#endif // !__wasm__
 
     if (!PROCAbortInitialize())
     {
diff --git a/src/coreclr/pal/src/loader/module.cpp b/src/coreclr/pal/src/loader/module.cpp
index d7cff970a0b02e..4c16bb98164b00 100644
--- a/src/coreclr/pal/src/loader/module.cpp
+++ b/src/coreclr/pal/src/loader/module.cpp
@@ -926,6 +926,22 @@ PAL_CopyModuleData(PVOID moduleBase, PVOID destinationBufferStart, PVOID destina
     }
     return param.result;
 }
+#elif defined(__wasm__)
+// TODO: get rid of whole module loading on wasm
+static int CopyModuleDataCallback(struct dl_phdr_info *info, size_t size, void *data)
+{
+    _ASSERTE("CopyModuleDataCallback not implemented for wasm");
+    return 0;
+}
+
+PALIMPORT
+int
+PALAPI
+PAL_CopyModuleData(PVOID moduleBase, PVOID destinationBufferStart, PVOID destinationBufferEnd)
+{
+    _ASSERTE("PAL_CopyModuleData not implemented for wasm");
+    return 0;
+}
 #else
 static int CopyModuleDataCallback(struct dl_phdr_info *info, size_t size, void *data)
 {
diff --git a/src/coreclr/pal/src/map/map.cpp b/src/coreclr/pal/src/map/map.cpp
index 8900ccd1058c01..6f33f4ad4dc399 100644
--- a/src/coreclr/pal/src/map/map.cpp
+++ b/src/coreclr/pal/src/map/map.cpp
@@ -1928,7 +1928,9 @@ MAPmmapAndRecord(
 
     // Ensure address and offset arguments mmap() are page-aligned.
     _ASSERTE(OffsetWithinPage(offset - adjust) == 0);
+#ifndef __wasm__
     _ASSERTE(OffsetWithinPage((off_t)pvBaseAddress) == 0);
+#endif
 
 #ifdef __APPLE__
     if ((prot & PROT_EXEC) != 0 && IsRunningOnMojaveHardenedRuntime())
@@ -1980,6 +1982,30 @@ MAPmmapAndRecord(
 
     }
     else
+#elif defined(__wasm__)
+    // WebAssembly doesn't support mmap() with location hints
+    if (pvBaseAddress != nullptr)
+    {
+        LPVOID pvMappedFile = mmap(NULL, len + adjust, prot, flags, fd, offset - adjust);
+        if (MAP_FAILED == pvMappedFile)
+        {
+            ERROR_(LOADER)("mmap failed with code %d: %s.\n", errno, strerror(errno));
+            palError = FILEGetLastErrorFromErrno();
+            return palError;
+        }
+        else
+        {
+            memcpy(pvBaseAddress, pvMappedFile, len + adjust);
+            if (-1 == munmap(pvMappedFile, len + adjust))
+            {
+                ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
+                if (NO_ERROR == palError)
+                    palError = FILEGetLastErrorFromErrno();
+                return palError;
+            }
+        }
+    }
+    else
 #endif
     {
         pvBaseAddress = mmap(pvBaseAddress, len + adjust, prot, flags, fd, offset - adjust);
@@ -2238,6 +2264,13 @@ void * MAPMapPEFile(HANDLE hFile, off_t offset)
     size_t headerSize;
     headerSize = GetVirtualPageSize(); // if there are lots of sections, this could be wrong
 
+#ifdef __wasm__
+    if (headerSize > ntHeader.OptionalHeader.SectionAlignment)
+    {
+        headerSize = ntHeader.OptionalHeader.SectionAlignment;
+    }
+#endif // __wasm__
+
     if (forceOveralign)
     {
         loadedBase = ALIGN_UP(loadedBase, ntHeader.OptionalHeader.SectionAlignment);
@@ -2297,7 +2330,11 @@ void * MAPMapPEFile(HANDLE hFile, off_t offset)
 
     void* prevSectionEndAligned;
     // the first "section" for our purposes is the header
+#ifndef __wasm__
     prevSectionEndAligned = ALIGN_UP((char*)loadedHeader + headerSize, GetVirtualPageSize());
+#else
+    prevSectionEndAligned = ALIGN_UP((char*)loadedHeader + headerSize, ntHeader.OptionalHeader.SectionAlignment);
+#endif // __wasm__
 
     for (unsigned i = 0; i < numSections; ++i)
     {
@@ -2329,12 +2366,14 @@ void * MAPMapPEFile(HANDLE hFile, off_t offset)
             goto doneReleaseMappingCriticalSection;
         }
 
+#ifndef __wasm__
         if (OffsetWithinPage((off_t)sectionBase) != OffsetWithinPage(offset + currentHeader.PointerToRawData))
         {
             ERROR_(LOADER)("can't map section if data and VA hint have different page alignment %d\n", i);
             palError = ERROR_INVALID_PARAMETER;
             goto doneReleaseMappingCriticalSection;
         }
+#endif // !__wasm__
 
         // Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
         if (prevSectionEndAligned < sectionBaseAligned)
@@ -2390,7 +2429,11 @@ void * MAPMapPEFile(HANDLE hFile, off_t offset)
         }
 #endif // _DEBUG
 
+#ifndef __wasm__
         prevSectionEndAligned = ALIGN_UP((char*)sectionBase + currentHeader.SizeOfRawData, GetVirtualPageSize()); // round up to page boundary
+#else
+        prevSectionEndAligned = ALIGN_UP((char*)sectionBase + currentHeader.SizeOfRawData, ntHeader.OptionalHeader.SectionAlignment); // round up to page boundary
+#endif // __wasm__
     }
 
     // Is there space after the last section and before the end of the mapped image? If so, add a PROT_NONE mapping to cover it.
diff --git a/src/coreclr/pal/src/map/virtual.cpp b/src/coreclr/pal/src/map/virtual.cpp
index dcf3fd5c51c852..7936c968c1664e 100644
--- a/src/coreclr/pal/src/map/virtual.cpp
+++ b/src/coreclr/pal/src/map/virtual.cpp
@@ -641,7 +641,7 @@ static LPVOID ReserveVirtualMemory(
     }
 #endif  // MMAP_ANON_IGNORES_PROTECTION
 
-#ifdef MADV_DONTDUMP
+#if defined(MADV_DONTDUMP) && !defined(TARGET_BROWSER)
     // Do not include reserved uncommitted memory in coredump.
     if (!(fAllocationType & MEM_COMMIT))
     {
@@ -730,14 +730,16 @@ VIRTUALCommitMemory(
 
     nProtect = W32toUnixAccessControl(flProtect);
 
+#ifndef TARGET_BROWSER
     // Commit the pages
     if (mprotect((void *) StartBoundary, MemSize, nProtect) != 0)
     {
         ERROR("mprotect() failed! Error(%d)=%s\n", errno, strerror(errno));
         goto error;
     }
+#endif
 
-#ifdef MADV_DODUMP
+#if defined(MADV_DONTDUMP) && !defined(TARGET_BROWSER)
     // Include committed memory in coredump. Any newly allocated memory included by default.
     if (!IsNewMemory)
     {
@@ -748,7 +750,9 @@ VIRTUALCommitMemory(
     pRetVal = (void *) StartBoundary;
     goto done;
 
+#ifndef TARGET_BROWSER
 error:
+#endif
     if ( flAllocationType & MEM_RESERVE || IsLocallyReserved )
     {
         munmap( pRetVal, MemSize );
@@ -1073,7 +1077,7 @@ VirtualFree(
             }
 #endif  // MMAP_ANON_IGNORES_PROTECTION
 
-#ifdef MADV_DONTDUMP
+#if defined(MADV_DONTDUMP) && !defined(TARGET_BROWSER)
             // Do not include freed memory in coredump.
             madvise((LPVOID) StartBoundary, MemSize, MADV_DONTDUMP);
 #endif
@@ -1207,9 +1211,11 @@ VirtualProtect(
     }
 
     pEntry = VIRTUALFindRegionInformation( StartBoundary );
+#ifndef TARGET_BROWSER
     if ( 0 == mprotect( (LPVOID)StartBoundary, MemSize,
                    W32toUnixAccessControl( flNewProtect ) ) )
     {
+#endif // !TARGET_BROWSER
         /* Reset the access protection. */
         TRACE( "Number of pages to change %d, starting page %d \n",
                NumberOfPagesToChange, OffSet );
@@ -1220,13 +1226,14 @@ VirtualProtect(
          */
         *lpflOldProtect = PAGE_EXECUTE_READWRITE;
 
-#ifdef MADV_DONTDUMP
+#if defined(MADV_DONTDUMP) && !defined(TARGET_BROWSER)
         // Include or exclude memory from coredump based on the protection.
         int advise = flNewProtect == PAGE_NOACCESS ? MADV_DONTDUMP : MADV_DODUMP;
         madvise((LPVOID)StartBoundary, MemSize, advise);
 #endif
 
         bRetVal = TRUE;
+#ifndef TARGET_BROWSER
     }
     else
     {
@@ -1240,6 +1247,7 @@ VirtualProtect(
             SetLastError( ERROR_INVALID_ACCESS );
         }
     }
+#endif // !TARGET_BROWSER
 ExitVirtualProtect:
     minipal_mutex_leave(&virtual_critsec);
 
diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp
index c56cc0637dcd2f..bc7e08acd502be 100644
--- a/src/coreclr/pal/src/thread/process.cpp
+++ b/src/coreclr/pal/src/thread/process.cpp
@@ -2610,6 +2610,7 @@ InitializeFlushProcessWriteBuffers()
     // Verify that the s_helperPage is really aligned to the GetVirtualPageSize()
     _ASSERTE((((SIZE_T)s_helperPage) & (GetVirtualPageSize() - 1)) == 0);
 
+#ifndef TARGET_BROWSER
     // Locking the page ensures that it stays in memory during the two mprotect
     // calls in the FlushProcessWriteBuffers below. If the page was unmapped between
     // those calls, they would not have the expected effect of generating IPI.
@@ -2619,6 +2620,9 @@ InitializeFlushProcessWriteBuffers()
     {
         return FALSE;
     }
+#else
+    int status;
+#endif // !TARGET_BROWSER
 
     status = pthread_mutex_init(&flushProcessWriteBuffersMutex, NULL);
     if (status != 0)
@@ -2664,18 +2668,22 @@ FlushProcessWriteBuffers()
         int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex);
         FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock");
 
+#ifndef TARGET_BROWSER
         // Changing a helper memory page protection from read / write to no access
         // causes the OS to issue IPI to flush TLBs on all processors. This also
         // results in flushing the processor buffers.
         status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_READ | PROT_WRITE);
         FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write");
+#endif // !TARGET_BROWSER
 
         // Ensure that the page is dirty before we change the protection so that
         // we prevent the OS from skipping the global TLB flush.
         InterlockedIncrement(s_helperPage);
 
+#ifndef TARGET_BROWSER
         status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_NONE);
         FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access");
+#endif // !TARGET_BROWSER
 
         status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
         FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp
index fe48f04dfe639b..9b53b9dcdeeafd 100644
--- a/src/coreclr/pal/src/thread/thread.cpp
+++ b/src/coreclr/pal/src/thread/thread.cpp
@@ -42,6 +42,10 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do
 #include 
 #endif
 
+#if defined(TARGET_BROWSER)
+#include 
+#endif
+
 #include 
 #include 
 #if HAVE_PTHREAD_NP_H
@@ -2281,6 +2285,10 @@ Return :
 BOOL
 CPalThread::EnsureSignalAlternateStack()
 {
+#ifdef __wasm__
+    // WebAssembly does not support alternate signal stacks.
+    return TRUE;
+#else // !__wasm__
     int st = 0;
 
     if (g_registered_signal_handlers)
@@ -2334,6 +2342,7 @@ CPalThread::EnsureSignalAlternateStack()
     }
 
     return (st == 0);
+#endif // !__wasm__
 }
 
 /*++
@@ -2351,6 +2360,7 @@ Return :
 void
 CPalThread::FreeSignalAlternateStack()
 {
+#ifndef __wasm__ // WebAssembly does not support alternate signal stacks.
     void *altstack = m_alternateStack;
     m_alternateStack = nullptr;
 
@@ -2374,6 +2384,7 @@ CPalThread::FreeSignalAlternateStack()
             }
         }
     }
+#endif // !__wasm__
 }
 
 #endif // !HAVE_MACH_EXCEPTIONS
@@ -2444,6 +2455,7 @@ CPalThread::GetStackBase()
     status = pthread_attr_init(&attr);
     _ASSERT_MSG(status == 0, "pthread_attr_init call failed");
 
+#ifndef TARGET_BROWSER
 #if HAVE_PTHREAD_ATTR_GET_NP
     status = pthread_attr_get_np(thread, &attr);
 #elif HAVE_PTHREAD_GETATTR_NP
@@ -2460,7 +2472,10 @@ CPalThread::GetStackBase()
     _ASSERT_MSG(status == 0, "pthread_attr_destroy call failed");
 
     stackBase = (void*)((size_t)stackAddr + stackSize);
-#endif
+#else // TARGET_BROWSER
+    stackBase = (void*)emscripten_stack_get_base();
+#endif // TARGET_BROWSER
+#endif // !TARGET_APPLE
 
     return stackBase;
 }
@@ -2484,6 +2499,7 @@ CPalThread::GetStackLimit()
     status = pthread_attr_init(&attr);
     _ASSERT_MSG(status == 0, "pthread_attr_init call failed");
 
+#ifndef TARGET_BROWSER
 #if HAVE_PTHREAD_ATTR_GET_NP
     status = pthread_attr_get_np(thread, &attr);
 #elif HAVE_PTHREAD_GETATTR_NP
@@ -2498,7 +2514,10 @@ CPalThread::GetStackLimit()
 
     status = pthread_attr_destroy(&attr);
     _ASSERT_MSG(status == 0, "pthread_attr_destroy call failed");
-#endif
+#else // TARGET_BROWSER
+    stackLimit = (void*)emscripten_stack_get_end();
+#endif // TARGET_BROWSER
+#endif // !TARGET_APPLE
 
     return stackLimit;
 }
diff --git a/src/coreclr/utilcode/clrhost.cpp b/src/coreclr/utilcode/clrhost.cpp
index a57b115fbfc780..41e8b2dc348b2a 100644
--- a/src/coreclr/utilcode/clrhost.cpp
+++ b/src/coreclr/utilcode/clrhost.cpp
@@ -62,7 +62,13 @@ DWORD GetClrModulePathName(SString& buffer)
 #ifdef HOST_WINDOWS
     return WszGetModuleFileName((HINSTANCE)GetClrModuleBase(), buffer);
 #else
-    return WszGetModuleFileName(PAL_GetPalHostModule(), buffer);
+#ifndef __wasm__
+    HMODULE hModule = PAL_GetPalHostModule();
+#else
+    // on wasm the PAL library is statically linked
+    HMODULE hModule = nullptr;
+#endif
+    return WszGetModuleFileName(hModule, buffer);
 #endif
 }
 
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index c0805d6fbc55a0..bc2ae12497ace7 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -902,6 +902,14 @@ elseif(CLR_CMAKE_TARGET_ARCH_RISCV64)
         ${ARCH_SOURCES_DIR}/singlestepper.cpp
         gcinfodecoder.cpp
     )
+elseif(CLR_CMAKE_TARGET_ARCH_WASM)
+    set(VM_SOURCES_WKS_ARCH
+        ${ARCH_SOURCES_DIR}/calldescrworkerwasm.cpp
+        ${ARCH_SOURCES_DIR}/profiler.cpp
+        ${ARCH_SOURCES_DIR}/helpers.cpp
+        exceptionhandling.cpp
+        gcinfodecoder.cpp
+    )
 endif()
 
 set(VM_SOURCES_DAC_ARCH
diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp
index 091a061ba5a863..e1605c6acff3b4 100644
--- a/src/coreclr/vm/assembly.cpp
+++ b/src/coreclr/vm/assembly.cpp
@@ -2392,7 +2392,7 @@ DebuggerAssemblyControlFlags Assembly::ComputeDebuggingConfig()
     IfFailThrow(GetDebuggingCustomAttributes(&dacfFlags));
     return (DebuggerAssemblyControlFlags)dacfFlags;
 #else // !DEBUGGING_SUPPORTED
-    return 0;
+    return (DebuggerAssemblyControlFlags)0;
 #endif // DEBUGGING_SUPPORTED
 }
 
diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp
index f5168fc0f799b1..7d9e533b9465d1 100644
--- a/src/coreclr/vm/callcounting.cpp
+++ b/src/coreclr/vm/callcounting.cpp
@@ -324,6 +324,8 @@ void CallCountingStub::StaticInitialize()
         // This should fail if the template is used on a platform which doesn't support the supported page size for templates
         ThrowHR(COR_E_EXECUTIONENGINE);
     }
+#elif defined(TARGET_WASM)
+    // CallCountingStub is not implemented on WASM
 #else
     _ASSERTE((SIZE_T)((BYTE*)CallCountingStubCode_End - (BYTE*)CallCountingStubCode) <= CallCountingStub::CodeSize);
 #endif
diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp
index 24389bc4e75420..95884af8c6ea3e 100644
--- a/src/coreclr/vm/callhelpers.cpp
+++ b/src/coreclr/vm/callhelpers.cpp
@@ -532,6 +532,10 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
 #endif
     callDescrData.fpReturnSize = fpReturnSize;
     callDescrData.pTarget = m_pCallTarget;
+#ifdef TARGET_WASM
+    callDescrData.pMD = m_pMD;
+    callDescrData.nArgsSize = m_argIt.GetArgSize();
+#endif
 
     CallDescrWorkerWithHandler(&callDescrData);
 
diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h
index 767ce238ac14fb..6454b580f96ccc 100644
--- a/src/coreclr/vm/callhelpers.h
+++ b/src/coreclr/vm/callhelpers.h
@@ -28,6 +28,10 @@ struct CallDescrData
 #endif
     UINT32                      fpReturnSize;
     PCODE                       pTarget;
+#ifdef TARGET_WASM
+    MethodDesc*                 pMD;
+    size_t                      nArgsSize;
+#endif
 
 #ifdef CALLDESCR_RETBUFFARGREG
     // Pointer to return buffer arg location
diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h
index 98e6eee147fcd6..1de315880bcca6 100644
--- a/src/coreclr/vm/callingconvention.h
+++ b/src/coreclr/vm/callingconvention.h
@@ -1223,6 +1223,9 @@ int ArgIteratorTemplate::GetNextOffset()
         m_idxGenReg = numRegistersUsed;
         m_ofsStack = 0;
         m_idxFPReg = 0;
+#elif defined(TARGET_WASM)
+        // we put everything on the stack, we don't have registers
+        m_ofsStack = 0;
 #else
         PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
 #endif
@@ -1794,6 +1797,13 @@ int ArgIteratorTemplate::GetNextOffset()
     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
     m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
 
+    return argOfs;
+#elif defined(TARGET_WASM)
+    int cbArg = ALIGN_UP(StackElemSize(argSize), TARGET_POINTER_SIZE);
+    int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
+    
+    m_ofsStack += cbArg;
+    
     return argOfs;
 #else
     PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp
index 30a70af7ace201..d6200bdb333524 100644
--- a/src/coreclr/vm/callstubgenerator.cpp
+++ b/src/coreclr/vm/callstubgenerator.cpp
@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-#ifdef FEATURE_INTERPRETER
+#if defined(FEATURE_INTERPRETER) && defined(FEATURE_JIT)
 
 #include "callstubgenerator.h"
 
@@ -997,4 +997,4 @@ void CallStubGenerator::ProcessArgument(ArgIterator& argIt, ArgLocDesc& argLocDe
 #endif // UNIX_AMD64_ABI
 }
 
-#endif // FEATURE_INTERPRETER
+#endif // FEATURE_INTERPRETER && FEATURE_JIT
diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp
index fb0ff126121562..79c61a4c311518 100644
--- a/src/coreclr/vm/ceemain.cpp
+++ b/src/coreclr/vm/ceemain.cpp
@@ -835,7 +835,9 @@ void EEStartupHelper()
         // Initialize the debugging services. This must be done before any
         // EE thread objects are created, and before any classes or
         // modules are loaded.
+#ifndef __wasm__
         InitializeDebugger(); // throws on error
+#endif
 #endif // DEBUGGING_SUPPORTED
 
 #ifdef PROFILING_SUPPORTED
@@ -912,7 +914,9 @@ void EEStartupHelper()
         }
 #endif
 
-#ifndef TARGET_WINDOWS
+        // on wasm we need to run finalizers on main thread as we are single threaded
+        // active issue: https://github.com/dotnet/runtime/issues/114096
+#if !defined(TARGET_WINDOWS) && !defined(__wasm__)
         // This isn't done as part of InitializeGarbageCollector() above because
         // debugger must be initialized before creating EE thread objects
         FinalizerThread::FinalizerThreadCreate();
diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp
index 9b95ef12f9de83..1704eeb1362418 100644
--- a/src/coreclr/vm/codeversion.cpp
+++ b/src/coreclr/vm/codeversion.cpp
@@ -12,9 +12,11 @@
 #ifdef FEATURE_CODE_VERSIONING
 #include "threadsuspend.h"
 #include "methoditer.h"
+#ifdef DDEBUGGING_SUPPORTED
 #include "../debug/ee/debugger.h"
 #include "../debug/ee/walker.h"
 #include "../debug/ee/controller.h"
+#endif // DDEBUGGING_SUPPORTED
 #endif // FEATURE_CODE_VERSIONING
 
 #ifndef FEATURE_CODE_VERSIONING
diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp
index 9b485ab803df19..578179d2e590e3 100644
--- a/src/coreclr/vm/corhost.cpp
+++ b/src/coreclr/vm/corhost.cpp
@@ -636,7 +636,7 @@ HRESULT CorHost2::CreateAppDomainWithManager(
             sAppPaths));
     }
 
-#if defined(TARGET_UNIX)
+#if defined(TARGET_UNIX) and !defined(TARGET_WASM)
     if (!g_coreclr_embedded)
     {
         // Check if the current code is executing in the single file host or in libcoreclr.so. The libSystem.Native is linked
diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp
index 00bfa6b96f6d63..48e8eddf25498f 100644
--- a/src/coreclr/vm/gcinfodecoder.cpp
+++ b/src/coreclr/vm/gcinfodecoder.cpp
@@ -2183,6 +2183,15 @@ template  void TGcInfoDecoder::ReportRe
     _ASSERTE( !"NYI" );
 }
 
+template  OBJECTREF* TGcInfoDecoder::GetCapturedRegister(
+    int             regNum,
+    PREGDISPLAY     pRD
+    )
+{
+    _ASSERTE( !"NYI" );
+    return nullptr;
+}
+
 #endif // Unknown platform
 
 #ifdef FEATURE_INTERPRETER
diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp
index a196dc871a625f..0d3f333e48ffd9 100644
--- a/src/coreclr/vm/interpexec.cpp
+++ b/src/coreclr/vm/interpexec.cpp
@@ -6,6 +6,8 @@
 #include "threads.h"
 #include "gcenv.h"
 #include "interpexec.h"
+
+#ifdef FEATURE_JIT
 #include "callstubgenerator.h"
 
 void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
@@ -45,6 +47,7 @@ void InvokeCompiledMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet)
 
     pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize);
 }
+#endif // FEATURE_JIT
 
 typedef void* (*HELPER_FTN_PP)(void*);
 typedef void* (*HELPER_FTN_BOX_UNBOX)(MethodTable*, void*);
@@ -1192,8 +1195,12 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
                     }
                     else if (codeInfo.GetCodeManager() != ExecutionManager::GetInterpreterCodeManager())
                     {
+#ifdef FEATURE_JIT
                         MethodDesc *pMD = codeInfo.GetMethodDesc();
                         InvokeCompiledMethod(pMD, stack + callArgsOffset, stack + returnOffset);
+#else
+                        EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, W("Attempted to execute non-interpreter code from interpreter on a non-JIT build"));
+#endif // FEATURE_JIT
                         break;
                     }
 
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index 1dcfce6a42ba39..37dd885542fb75 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -201,6 +201,7 @@ DEFINE_METASIG_T(SM(Exception_Obj_RefIntPtr_RetVoidPtr, C(EXCEPTION) j r(I), P(v
 #endif // FEATURE_OBJCMARSHAL
 DEFINE_METASIG(SM(Int_RetVoid, i, v))
 DEFINE_METASIG(SM(Int_RetObj, i, j))
+DEFINE_METASIG(SM(Int_RetInt, i, i))
 DEFINE_METASIG(SM(Int_Int_RetVoid, i i, v))
 DEFINE_METASIG(SM(Str_RetIntPtr, s, I))
 DEFINE_METASIG(SM(Str_RetBool, s, F))
diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp
index 798e9849de3a6a..6e845484cd73e5 100644
--- a/src/coreclr/vm/precode.cpp
+++ b/src/coreclr/vm/precode.cpp
@@ -133,6 +133,9 @@ MethodDesc* Precode::GetMethodDesc(BOOL fSpeculative /*= FALSE*/)
     TADDR pMD = (TADDR)NULL;
 
     PrecodeType precodeType = GetType();
+#ifdef __wasm__
+    pMD = *(TADDR*)(m_data + OFFSETOF_PRECODE_TYPE + 4);
+#else
     switch (precodeType)
     {
     case PRECODE_STUB:
@@ -165,6 +168,7 @@ MethodDesc* Precode::GetMethodDesc(BOOL fSpeculative /*= FALSE*/)
     default:
         break;
     }
+#endif // __wasm__
 
     if (pMD == (TADDR)NULL)
     {
@@ -287,6 +291,10 @@ void Precode::Init(Precode* pPrecodeRX, PrecodeType t, MethodDesc* pMD, LoaderAl
 {
     LIMITED_METHOD_CONTRACT;
 
+#ifdef __wasm__  
+    m_data[OFFSETOF_PRECODE_TYPE] = t;
+    *(TADDR*)(m_data + OFFSETOF_PRECODE_TYPE + 4) = (TADDR)pMD;
+#else
     switch (t) {
     case PRECODE_STUB:
         ((StubPrecode*)this)->Init((StubPrecode*)pPrecodeRX, (TADDR)pMD, pLoaderAllocator);
@@ -310,6 +318,7 @@ void Precode::Init(Precode* pPrecodeRX, PrecodeType t, MethodDesc* pMD, LoaderAl
         UnexpectedPrecodeType("Precode::Init", t);
         break;
     }
+#endif
 
     _ASSERTE(IsValidType(GetType()));
 }
@@ -531,6 +540,8 @@ void StubPrecode::StaticInitialize()
     }
 
     #undef ENUM_PAGE_SIZE
+#elif defined(TARGET_WASM)
+    // StubPrecode is not implemented on WASM
 #else
     _ASSERTE((SIZE_T)((BYTE*)StubPrecodeCode_End - (BYTE*)StubPrecodeCode) <= StubPrecode::CodeSize);
 #endif
@@ -676,6 +687,8 @@ void FixupPrecode::StaticInitialize()
         // This should fail if the template is used on a platform which doesn't support the supported page size for templates
         ThrowHR(COR_E_EXECUTIONENGINE);
     }
+#elif defined(TARGET_WASM)
+    // FixupPrecode is not implemented on WASM
 #else
     _ASSERTE((SIZE_T)((BYTE*)FixupPrecodeCode_End - (BYTE*)FixupPrecodeCode) <= FixupPrecode::CodeSize);
 #endif
@@ -683,6 +696,8 @@ void FixupPrecode::StaticInitialize()
     _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type);
 #elif TARGET_RISCV64
     _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == FixupPrecode::Type);
+#elif defined(TARGET_WASM)
+    // FixupPrecode is not implemented on WASM
 #else
     _ASSERTE(*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE) == FixupPrecode::Type);
 #endif
diff --git a/src/coreclr/vm/precode.h b/src/coreclr/vm/precode.h
index 64394d259e91a4..bff78896df4033 100644
--- a/src/coreclr/vm/precode.h
+++ b/src/coreclr/vm/precode.h
@@ -52,7 +52,7 @@ EXTERN_C VOID STDCALL PrecodeRemotingThunk();
 
 #elif defined(TARGET_WASM)
 
-#define SIZEOF_PRECODE_BASE         0
+#define SIZEOF_PRECODE_BASE         8 // 2*pointer size
 #define OFFSETOF_PRECODE_TYPE       0
 
 #endif // TARGET_AMD64
@@ -77,7 +77,6 @@ struct InvalidPrecode
 #elif defined(TARGET_RISCV64)
     static const int Type = 0xff;
 #elif defined(TARGET_WASM)
-    // unreachable instruction
     static const int Type = 0;
 #endif
 };
@@ -131,7 +130,7 @@ struct StubPrecode
     static const SIZE_T CodeSize = 24;
 #elif defined(TARGET_WASM)
     static const int Type = 0;
-    static const SIZE_T CodeSize = 0;
+    static const SIZE_T CodeSize = 8;
 #endif // TARGET_AMD64
 
     BYTE m_code[CodeSize];
@@ -413,7 +412,7 @@ struct FixupPrecode
     static const int FixupCodeOffset = 10;
 #elif defined(TARGET_WASM)
     static const int Type = 2;
-    static const SIZE_T CodeSize = 0;
+    static const SIZE_T CodeSize = 8;
     static const int FixupCodeOffset = 0;
 #endif // TARGET_AMD64
 
@@ -563,6 +562,10 @@ inline BYTE StubPrecode::GetType()
     LIMITED_METHOD_DAC_CONTRACT;
     TADDR type = GetData()->Type;
 
+#ifdef __wasm__
+    return (BYTE)type;
+#endif
+
     // There are a limited number of valid bit patterns here. Restrict to those, so that the
     // speculative variant of GetPrecodeFromEntryPoint is more robust. Type is stored as a TADDR
     // so that a single byte matching is not enough to cause a false match.
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 2f5c506ccb6000..d7c79143cb5093 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1990,7 +1990,7 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method
 }
 
 #ifdef FEATURE_INTERPRETER
-extern "C" void STDCALL ExecuteInterpretedMethod(TransitionBlock* pTransitionBlock, TADDR byteCodeAddr)
+extern "C" void STDCALL ExecuteInterpretedMethodWithArgs(TransitionBlock* pTransitionBlock, TADDR byteCodeAddr, int8_t* pArgs, size_t size, int8_t *pReturnValue, size_t returnValueSize)
 {
     // Argument registers are in the TransitionBlock
     // The stack arguments are right after the pTransitionBlock
@@ -2002,6 +2002,12 @@ extern "C" void STDCALL ExecuteInterpretedMethod(TransitionBlock* pTransitionBlo
     }
     int8_t *sp = threadContext->pStackPointer;
 
+    // copy the arguments to the stack
+    if (size > 0 && pArgs != nullptr)
+    {
+        memcpy(sp, pArgs, size);
+    }
+
     // This construct ensures that the InterpreterFrame is always stored at a higher address than the
     // InterpMethodContextFrame. This is important for the stack walking code.
     struct Frames
@@ -2024,8 +2030,19 @@ extern "C" void STDCALL ExecuteInterpretedMethod(TransitionBlock* pTransitionBlo
 
     InterpExecMethod(&frames.interpreterFrame, &frames.interpMethodContextFrame, threadContext);
 
+    if (pReturnValue != nullptr && returnValueSize > 0)
+    {
+        // Copy the return value from the stack to the returnValue pointer
+        memcpy(pReturnValue, &retVal, returnValueSize);
+    }
+
     frames.interpreterFrame.Pop();
 }
+
+extern "C" void STDCALL ExecuteInterpretedMethod(TransitionBlock* pTransitionBlock, TADDR byteCodeAddr)
+{
+    ExecuteInterpretedMethodWithArgs(pTransitionBlock, byteCodeAddr, nullptr, 0, nullptr, 0);
+}
 #endif // FEATURE_INTERPRETER
 
 #ifdef _DEBUG
diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp
index b86922e7fec1c5..2c768ebce782dc 100644
--- a/src/coreclr/vm/threads.cpp
+++ b/src/coreclr/vm/threads.cpp
@@ -6067,11 +6067,13 @@ BOOL Thread::SetStackLimits(SetStackLimitScope scope)
     {
         m_CacheStackBase  = GetStackUpperBound();
         m_CacheStackLimit = GetStackLowerBound();
+#ifndef __wasm__ // stack can start at address 0 on wasm/emscripten and usually does
         if (m_CacheStackLimit == NULL)
         {
             _ASSERTE(!"Failed to set stack limits");
             return FALSE;
         }
+#endif
 
         // Compute the limit used by TryEnsureSufficientExecutionStack and cache it on the thread. This minimum stack size should
         // be sufficient to allow a typical non-recursive call chain to execute, including potential exception handling and
diff --git a/src/coreclr/vm/wasm/calldescrworkerwasm.cpp b/src/coreclr/vm/wasm/calldescrworkerwasm.cpp
new file mode 100644
index 00000000000000..8e8811f0df095d
--- /dev/null
+++ b/src/coreclr/vm/wasm/calldescrworkerwasm.cpp
@@ -0,0 +1,16 @@
+#include 
+
+extern "C" void STDCALL ExecuteInterpretedMethodWithArgs(TransitionBlock* pTransitionBlock, TADDR byteCodeAddr, int8_t* pArgs, size_t size, int8_t* pReturnValue, size_t returnValueSize);
+
+extern "C" void STDCALL CallDescrWorkerInternal(CallDescrData * pCallDescrData)
+{
+    PCODE code = pCallDescrData->pMD->GetNativeCode();
+    if (!code)
+    {
+        GCX_PREEMP();
+        pCallDescrData->pMD->PrepareInitialCode(CallerGCMode::Coop);
+        code = pCallDescrData->pMD->GetNativeCode();
+    }
+
+    ExecuteInterpretedMethodWithArgs(((TransitionBlock*)pCallDescrData->pSrc) - 1, code, (int8_t*)pCallDescrData->pSrc, pCallDescrData->nArgsSize, (int8_t*)pCallDescrData->returnValue, sizeof(pCallDescrData->returnValue));
+}
diff --git a/src/coreclr/vm/wasm/cgencpu.h b/src/coreclr/vm/wasm/cgencpu.h
index 2093a7a80a5499..ca996b269a1859 100644
--- a/src/coreclr/vm/wasm/cgencpu.h
+++ b/src/coreclr/vm/wasm/cgencpu.h
@@ -85,9 +85,13 @@ struct ArgumentRegisters {
 class StubLinkerCPU : public StubLinker
 {
 public:
-    static void Init();
-    void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray);
-    VOID EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg);
+    static void Init() { /* no-op on wasm */ }
+    inline void EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray) {
+        _ASSERTE("The EmitShuffleThunk is not implemented on wasm");
+    }
+    inline VOID EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg) {
+        _ASSERTE("The EmitComputedInstantiatingMethodStub is not implemented on wasm");
+    }
 };
 
 //**********************************************************************
@@ -162,4 +166,26 @@ FORCEINLINE int64_t PalInterlockedCompareExchange64(_Inout_ int64_t volatile *pD
     return result;
 }
 
+inline void SetFirstArgReg(T_CONTEXT *context, TADDR value)
+{
+    PORTABILITY_ASSERT("SetFirstArgReg is not implemented on wasm");
+}
+
+inline TADDR GetFirstArgReg(T_CONTEXT *context)
+{
+    PORTABILITY_ASSERT("GetFirstArgReg is not implemented on wasm");
+    return 0;
+}
+
+inline void SetSecondArgReg(T_CONTEXT *context, TADDR value)
+{
+    PORTABILITY_ASSERT("SetSecondArgReg is not implemented on wasm");
+}
+
+inline TADDR GetSecondArgReg(T_CONTEXT *context)
+{
+    PORTABILITY_ASSERT("GetSecondArgReg is not implemented on wasm");
+    return 0;
+}
+
 #endif // __cgenwasm_h__
diff --git a/src/coreclr/vm/wasm/excepcpu.h b/src/coreclr/vm/wasm/excepcpu.h
index 7ca5a994a60760..bb819cae090f5d 100644
--- a/src/coreclr/vm/wasm/excepcpu.h
+++ b/src/coreclr/vm/wasm/excepcpu.h
@@ -29,6 +29,9 @@ PCODE GetAdjustedCallAddress(PCODE returnAddress)
     return returnAddress;
 }
 
-BOOL AdjustContextForVirtualStub(EXCEPTION_RECORD *pExceptionRecord, T_CONTEXT *pContext);
+inline BOOL AdjustContextForVirtualStub(EXCEPTION_RECORD *pExceptionRecord, T_CONTEXT *pContext) {
+    _ASSERTE("AdjustContextForVirtualStub is not implemented on wasm");
+    return FALSE;
+}
 
 #endif // __excepcpu_h__
diff --git a/src/coreclr/vm/wasm/gmscpu.h b/src/coreclr/vm/wasm/gmscpu.h
index 5355cfbc546ee6..563a80a899c947 100644
--- a/src/coreclr/vm/wasm/gmscpu.h
+++ b/src/coreclr/vm/wasm/gmscpu.h
@@ -41,7 +41,10 @@
         static void unwindLazyState(LazyMachState* baseState,
                                     MachState* lazyState,
                                     DWORD threadId,
-                                    int funCallDepth = 1);
+                                    int funCallDepth = 1)
+        {
+            _ASSERTE("LazyMachState::unwindLazyState is not implemented");
+        }
     
         friend class HelperMethodFrame;
         friend class CheckAsmOffsets;
@@ -60,7 +63,10 @@
     // as light weight as possible, as we may never need the state that
     // we capture.  Thus to complete the process you need to call
     // 'getMachState()', which finishes the process
-    EXTERN_C void LazyMachStateCaptureState(struct LazyMachState *pState);
+    EXTERN_C inline void LazyMachStateCaptureState(struct LazyMachState *pState)
+    {
+        _ASSERTE("LazyMachStateCaptureState is not implemented");
+    }
     
     // CAPTURE_STATE captures just enough register state so that the state of the
     // processor can be deterined just after the routine that has CAPTURE_STATE in
diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp
new file mode 100644
index 00000000000000..0af3f891355f86
--- /dev/null
+++ b/src/coreclr/vm/wasm/helpers.cpp
@@ -0,0 +1,412 @@
+extern "C" void STDCALL CallCountingStubCode()
+{
+    PORTABILITY_ASSERT("CallCountingStubCode is not implemented on wasm");
+}
+
+extern "C" void CallCountingStubCode_End()
+{
+    PORTABILITY_ASSERT("CallCountingStubCode_End is not implemented on wasm");
+}
+
+extern "C" void STDCALL OnCallCountThresholdReachedStub()
+{
+    PORTABILITY_ASSERT("OnCallCountThresholdReachedStub is not implemented on wasm");
+}
+
+extern "C" void STDCALL ThePreStub()
+{
+    PORTABILITY_ASSERT("ThePreStub is not implemented on wasm");
+}
+
+extern "C" void InterpreterStub()
+{
+    PORTABILITY_ASSERT("InterpreterStub is not implemented on wasm");
+}
+
+extern "C" UINT_PTR STDCALL GetCurrentIP(void)
+{
+    PORTABILITY_ASSERT("GetCurrentIP is not implemented on wasm");
+    return 0;
+}
+
+extern "C" void STDMETHODCALLTYPE JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle)
+{
+    PORTABILITY_ASSERT("JIT_ProfilerEnterLeaveTailcallStub is not implemented on wasm");
+}
+
+extern "C" void STDCALL DelayLoad_MethodCall()
+{
+    PORTABILITY_ASSERT("DelayLoad_MethodCall is not implemented on wasm");
+}
+
+extern "C" void STDCALL DelayLoad_Helper()
+{
+    PORTABILITY_ASSERT("DelayLoad_Helper is not implemented on wasm");
+}
+
+extern "C" void STDCALL DelayLoad_Helper_Obj()
+{
+    PORTABILITY_ASSERT("DelayLoad_Helper_Obj is not implemented on wasm");
+}
+
+extern "C" void STDCALL DelayLoad_Helper_ObjObj()
+{
+    PORTABILITY_ASSERT("DelayLoad_Helper_ObjObj is not implemented on wasm");
+}
+
+extern "C" void STDCALL NDirectImportThunk()
+{
+    PORTABILITY_ASSERT("NDirectImportThunk is not implemented on wasm");
+}
+
+extern "C" void STDCALL StubPrecodeCode()
+{
+    PORTABILITY_ASSERT("StubPrecodeCode is not implemented on wasm");
+}
+
+extern "C" void STDCALL StubPrecodeCode_End()
+{
+    PORTABILITY_ASSERT("StubPrecodeCode_End is not implemented on wasm");
+}
+
+extern "C" void STDCALL FixupPrecodeCode()
+{
+    PORTABILITY_ASSERT("FixupPrecodeCode is not implemented on wasm");
+}
+
+extern "C" void STDCALL FixupPrecodeCode_End()
+{
+    PORTABILITY_ASSERT("FixupPrecodeCode_End is not implemented on wasm");
+}
+
+extern "C" void STDCALL JIT_PatchedCodeLast()
+{
+    PORTABILITY_ASSERT("JIT_PatchedCodeLast is not implemented on wasm");
+}
+
+extern "C" void STDCALL JIT_PatchedCodeStart()
+{
+    PORTABILITY_ASSERT("JIT_PatchedCodeStart is not implemented on wasm");
+}
+
+extern "C" void RhpInitialInterfaceDispatch()
+{
+    PORTABILITY_ASSERT("RhpInitialInterfaceDispatch is not implemented on wasm");
+}
+
+unsigned FuncEvalFrame::GetFrameAttribs_Impl(void)
+{
+    PORTABILITY_ASSERT("FuncEvalFrame::GetFrameAttribs_Impl is not implemented on wasm");
+    return 0;
+}
+
+TADDR FuncEvalFrame::GetReturnAddressPtr_Impl()
+{
+    PORTABILITY_ASSERT("FuncEvalFrame::GetReturnAddressPtr_Impl is not implemented on wasm");
+    return 0;
+}
+
+void FuncEvalFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats)
+{
+    PORTABILITY_ASSERT("FuncEvalFrame::UpdateRegDisplay_Impl is not implemented on wasm");
+}
+
+void InlinedCallFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats)
+{
+    PORTABILITY_ASSERT("InlinedCallFrame::UpdateRegDisplay_Impl is not implemented on wasm");
+}
+
+void FaultingExceptionFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats)
+{
+    PORTABILITY_ASSERT("FaultingExceptionFrame::UpdateRegDisplay_Impl is not implemented on wasm");
+}
+
+void HelperMethodFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats)
+{
+    PORTABILITY_ASSERT("HelperMethodFrame::UpdateRegDisplay_Impl is not implemented on wasm");
+}
+
+void TransitionFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats)
+{
+    PORTABILITY_ASSERT("TransitionFrame::UpdateRegDisplay_Impl is not implemented on wasm");
+}
+
+size_t CallDescrWorkerInternalReturnAddressOffset;
+
+VOID PALAPI RtlRestoreContext(IN PCONTEXT ContextRecord, IN PEXCEPTION_RECORD ExceptionRecord)
+{
+    PORTABILITY_ASSERT("RtlRestoreContext is not implemented on wasm");
+}
+
+extern "C" void TheUMEntryPrestub(void)
+{
+    PORTABILITY_ASSERT("TheUMEntryPrestub is not implemented on wasm");
+}
+
+extern "C" void STDCALL VarargPInvokeStub(void)
+{
+    PORTABILITY_ASSERT("VarargPInvokeStub is not implemented on wasm");
+}
+
+extern "C" void STDCALL VarargPInvokeStub_RetBuffArg(void)
+{
+    PORTABILITY_ASSERT("VarargPInvokeStub_RetBuffArg is not implemented on wasm");
+}
+
+extern "C" PCODE CID_VirtualOpenDelegateDispatch(TransitionBlock * pTransitionBlock)
+{
+    PORTABILITY_ASSERT("CID_VirtualOpenDelegateDispatch is not implemented on wasm");
+    return 0;
+}
+
+extern "C" FCDECL2(VOID, JIT_WriteBarrier_Callable, Object **dst, Object *ref)
+{
+    PORTABILITY_ASSERT("JIT_WriteBarrier_Callable is not implemented on wasm");
+}
+
+EXTERN_C void JIT_WriteBarrier_End()
+{
+    PORTABILITY_ASSERT("JIT_WriteBarrier_End is not implemented on wasm");
+}
+
+EXTERN_C void JIT_CheckedWriteBarrier_End()
+{
+    PORTABILITY_ASSERT("JIT_CheckedWriteBarrier_End is not implemented on wasm");
+}
+
+EXTERN_C void JIT_ByRefWriteBarrier_End()
+{
+    PORTABILITY_ASSERT("JIT_ByRefWriteBarrier_End is not implemented on wasm");
+}
+
+EXTERN_C void JIT_StackProbe_End()
+{
+    PORTABILITY_ASSERT("JIT_StackProbe_End is not implemented on wasm");
+}
+
+EXTERN_C int __fastcall HelperMethodFrameRestoreState(
+    INDEBUG_COMMA(HelperMethodFrame *pFrame)
+    MachState *pState
+)
+{
+    PORTABILITY_ASSERT("HelperMethodFrameRestoreState is not implemented on wasm");
+    return 0;
+}
+
+EXTERN_C VOID STDCALL ResetCurrentContext()
+{
+    PORTABILITY_ASSERT("ResetCurrentContext is not implemented on wasm");
+}
+
+extern "C" void STDCALL GenericPInvokeCalliHelper(void)
+{
+    PORTABILITY_ASSERT("GenericPInvokeCalliHelper is not implemented on wasm");
+}
+
+EXTERN_C void JIT_PInvokeBegin(InlinedCallFrame* pFrame)
+{
+    PORTABILITY_ASSERT("JIT_PInvokeBegin is not implemented on wasm");
+}
+
+EXTERN_C void JIT_PInvokeEnd(InlinedCallFrame* pFrame)
+{
+    PORTABILITY_ASSERT("JIT_PInvokeEnd is not implemented on wasm");
+}
+
+extern "C" void STDCALL JIT_StackProbe()
+{
+    PORTABILITY_ASSERT("JIT_StackProbe is not implemented on wasm");
+}
+
+EXTERN_C FCDECL0(void, JIT_PollGC)
+{
+    PORTABILITY_ASSERT("JIT_PollGC is not implemented on wasm");
+}
+
+extern "C" FCDECL2(VOID, JIT_WriteBarrier, Object **dst, Object *ref)
+{
+    PORTABILITY_ASSERT("JIT_WriteBarrier is not implemented on wasm");
+}
+
+extern "C" FCDECL2(VOID, JIT_CheckedWriteBarrier, Object **dst, Object *ref)
+{
+    PORTABILITY_ASSERT("JIT_CheckedWriteBarrier is not implemented on wasm");
+}
+
+extern "C" void STDCALL JIT_ByRefWriteBarrier()
+{
+    PORTABILITY_ASSERT("JIT_ByRefWriteBarrier is not implemented on wasm");
+}
+
+void InitJITHelpers1()
+{
+    /* no-op TODO do we need to do anything for the interpreter? */
+}
+
+extern "C" HRESULT __cdecl CorDBGetInterface(DebugInterface** rcInterface)
+{
+    PORTABILITY_ASSERT("CorDBGetInterface is not implemented on wasm");
+    return 0;
+}
+
+extern "C" void RhpInterfaceDispatch1()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch1 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatch2()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch2 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatch4()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch4 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatch8()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch8 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatch16()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch16 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatch32()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch32 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatch64()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatch64 is not implemented on wasm");
+}
+
+extern "C" void RhpVTableOffsetDispatch()
+{
+    PORTABILITY_ASSERT("RhpVTableOffsetDispatch is not implemented on wasm");
+}
+
+typedef uint8_t CODE_LOCATION;
+CODE_LOCATION RhpAssignRefAVLocation;
+CODE_LOCATION RhpCheckedAssignRefAVLocation;
+CODE_LOCATION RhpByRefAssignRefAVLocation1;
+CODE_LOCATION RhpByRefAssignRefAVLocation2;
+
+extern "C" void ThisPtrRetBufPrecodeWorker()
+{
+    PORTABILITY_ASSERT("ThisPtrRetBufPrecodeWorker is not implemented on wasm");
+}
+
+extern "C" FCDECL2(VOID, RhpAssignRef, Object **dst, Object *ref)
+{
+    PORTABILITY_ASSERT("RhpAssignRef is not implemented on wasm");
+}
+
+extern "C" FCDECL2(VOID, RhpCheckedAssignRef, Object **dst, Object *ref)
+{
+    PORTABILITY_ASSERT("RhpCheckedAssignRef is not implemented on wasm");
+}
+
+extern "C" FCDECL2(VOID, RhpByRefAssignRef, Object **dst, Object *ref)
+{
+    PORTABILITY_ASSERT("RhpByRefAssignRef is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation1()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation1 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation2()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation2 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation4()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation4 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation8()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation8 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation16()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation16 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation32()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation32 is not implemented on wasm");
+}
+
+extern "C" void RhpInterfaceDispatchAVLocation64()
+{
+    PORTABILITY_ASSERT("RhpInterfaceDispatchAVLocation64 is not implemented on wasm");
+}
+
+extern "C" void RhpVTableOffsetDispatchAVLocation()
+{
+    PORTABILITY_ASSERT("RhpVTableOffsetDispatchAVLocation is not implemented on wasm");
+}
+
+extern "C" void STDCALL ThePreStubPatchLabel(void)
+{
+    PORTABILITY_ASSERT("ThePreStubPatchLabel is not implemented on wasm");
+}
+
+LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv)
+{
+    PORTABILITY_ASSERT("CLRNoCatchHandler is not implemented on wasm");
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
+EXTERN_C void STDMETHODCALLTYPE ProfileEnterNaked(FunctionIDOrClientID functionIDOrClientID)
+{
+    PORTABILITY_ASSERT("ProfileEnterNaked is not implemented on wasm");
+}
+
+EXTERN_C void STDMETHODCALLTYPE ProfileLeaveNaked(UINT_PTR clientData)
+{
+    PORTABILITY_ASSERT("ProfileLeaveNaked is not implemented on wasm");
+}
+
+EXTERN_C void STDMETHODCALLTYPE ProfileTailcallNaked(UINT_PTR clientData)
+{
+    PORTABILITY_ASSERT("ProfileTailcallNaked is not implemented on wasm");
+}
+
+int StompWriteBarrierEphemeral(bool isRuntimeSuspended)
+{
+    _ASSERTE("StompWriteBarrierEphemeral is not implemented on wasm");
+    return 0;
+}
+
+int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
+{
+    _ASSERTE("StompWriteBarrierResize is not implemented on wasm");
+    return 0;
+}
+
+void FlushWriteBarrierInstructionCache()
+{
+    PORTABILITY_ASSERT("FlushWriteBarrierInstructionCache is not implemented on wasm");
+}
+
+EXTERN_C Thread * JIT_InitPInvokeFrame(InlinedCallFrame *pFrame)
+{
+    PORTABILITY_ASSERT("JIT_InitPInvokeFrame is not implemented on wasm");
+    return nullptr;
+}
+
+void _DacGlobals::Initialize()
+{
+    /* no-op on wasm */
+}
+
+int g_pDebugger;
diff --git a/src/coreclr/vm/wasm/profiler.cpp b/src/coreclr/vm/wasm/profiler.cpp
new file mode 100644
index 00000000000000..b4004f1c4c4347
--- /dev/null
+++ b/src/coreclr/vm/wasm/profiler.cpp
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+#ifdef PROFILING_SUPPORTED
+#include "asmconstants.h"
+#include "proftoeeinterfaceimpl.h"
+
+UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void* pPlatformSpecificHandle)
+{
+    _ASSERTE(!"ProfileGetIPFromPlatformSpecificHandle is not implemented on wasm");
+    return 0;
+}
+
+void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, FunctionID functionId)
+{
+    _ASSERTE(!"ProfileSetFunctionIDInPlatformSpecificHandle is not implemented on wasm");
+}
+
+ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHandle)
+    : m_argIterator(pSig)
+{
+    _ASSERTE(!"ProfileArgIterator constructor is not implemented on wasm");
+}
+
+ProfileArgIterator::~ProfileArgIterator()
+{
+    _ASSERTE(!"ProfileArgIterator destructor is not implemented on wasm");
+}
+
+LPVOID ProfileArgIterator::GetNextArgAddr()
+{
+    _ASSERTE(!"GetNextArgAddr is not implemented on wasm");
+    return nullptr;
+}
+
+LPVOID ProfileArgIterator::GetHiddenArgValue(void)
+{
+    _ASSERTE(!"GetHiddenArgValue is not implemented on wasm");
+    return nullptr;
+}
+
+LPVOID ProfileArgIterator::GetThis(void)
+{
+    _ASSERTE(!"GetThis is not implemented on wasm");
+    return nullptr;
+}
+
+LPVOID ProfileArgIterator::GetReturnBufferAddr(void)
+{
+    _ASSERTE(!"GetReturnBufferAddr is not implemented on wasm");
+    return nullptr;
+}
+
+
+#endif // PROFILING_SUPPORTED
diff --git a/src/native/libs/CMakeLists.txt b/src/native/libs/CMakeLists.txt
index c90e9b8a45ddc5..a54fa43ed3f902 100644
--- a/src/native/libs/CMakeLists.txt
+++ b/src/native/libs/CMakeLists.txt
@@ -4,6 +4,10 @@ include(CheckCCompilerFlag)
 
 project(LibsNative C)
 
+if (MONO_RUNTIME)
+    add_definitions(-DMONO_RUNTIME)
+endif()
+
 if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
     # This is our root CMakeList.txt, so we need to set up some global settings and include the minipal here.
     include(../../../eng/native/configurepaths.cmake)
diff --git a/src/native/libs/System.Globalization.Native/entrypoints.c b/src/native/libs/System.Globalization.Native/entrypoints.c
index f2db315ec5452e..9adb279383b8ad 100644
--- a/src/native/libs/System.Globalization.Native/entrypoints.c
+++ b/src/native/libs/System.Globalization.Native/entrypoints.c
@@ -41,8 +41,10 @@ static const Entry s_globalizationNative[] =
     DllImportEntry(GlobalizationNative_GetSortHandle)
     DllImportEntry(GlobalizationNative_GetSortKey)
     DllImportEntry(GlobalizationNative_GetSortVersion)
+#ifndef __wasm__
     DllImportEntry(GlobalizationNative_GetTimeZoneDisplayName)
     DllImportEntry(GlobalizationNative_IanaIdToWindowsId)
+#endif
     DllImportEntry(GlobalizationNative_IndexOf)
     DllImportEntry(GlobalizationNative_InitICUFunctions)
     DllImportEntry(GlobalizationNative_IsNormalized)
@@ -54,7 +56,9 @@ static const Entry s_globalizationNative[] =
 #endif
     DllImportEntry(GlobalizationNative_NormalizeString)
     DllImportEntry(GlobalizationNative_StartsWith)
+#ifndef __wasm__
     DllImportEntry(GlobalizationNative_WindowsIdToIanaId)
+#endif
 #if defined(APPLE_HYBRID_GLOBALIZATION)
     DllImportEntry(GlobalizationNative_ChangeCaseInvariantNative)
     DllImportEntry(GlobalizationNative_ChangeCaseNative)
diff --git a/src/native/libs/build-native.proj b/src/native/libs/build-native.proj
index 2b5dcac9ae4a41..7380913b8ed279 100644
--- a/src/native/libs/build-native.proj
+++ b/src/native/libs/build-native.proj
@@ -98,9 +98,10 @@
           Condition="$([MSBuild]::IsOsPlatform(Windows))">
     
         <_BuildNativeArgs Condition="'$(Ninja)' == 'false'">$(_BuildNativeArgs) msbuild
+        <_MonoCMakeArgs Condition="'$(RuntimeFlavor)' == 'Mono'">-DMONO_RUNTIME=1
         <_MonoWasmMTCMakeArgs Condition="'$(WasmEnableThreads)' == 'true'">-DMONO_WASM_MT=1
         <_MonoWasmMTCMakeArgs Condition="'$(CMakeArgs)' != ''"> $(_MonoWasmMTCMakeArgs)
-        <_BuildNativeArgs Condition="'$(CMakeArgs)' != '' or '$(_MonoWasmMTCMakeArgs)' != ''">$(_BuildNativeArgs) -cmakeargs "$(CMakeArgs)$(_MonoWasmMTCMakeArgs)"
+        <_BuildNativeArgs Condition="'$(CMakeArgs)' != '' or '$(_MonoWasmMTCMakeArgs)' != '' or '$(_MonoCMakeArgs)' != ''">$(_BuildNativeArgs) -cmakeargs "$(CMakeArgs)$(_MonoCMakeArgs)$(_MonoWasmMTCMakeArgs)"
     
     
     
diff --git a/src/native/minipal/random.c b/src/native/minipal/random.c
index 3c2b33ac7463b0..e26ce66d6660c2 100644
--- a/src/native/minipal/random.c
+++ b/src/native/minipal/random.c
@@ -77,7 +77,7 @@ int32_t minipal_get_cryptographically_secure_random_bytes(uint8_t* buffer, int32
 {
     assert(buffer != NULL);
 
-#ifdef __EMSCRIPTEN__
+#if defined(__EMSCRIPTEN__) && defined(MONO_RUNTIME)
     extern int32_t mono_wasm_browser_entropy(uint8_t* buffer, int32_t bufferLength);
     static bool sMissingBrowserCrypto;
     if (!sMissingBrowserCrypto)