diff --git a/CLR-Host-Notes.md b/CLR-Host-Notes.md
new file mode 100644
index 00000000000..325fa4e7e48
--- /dev/null
+++ b/CLR-Host-Notes.md
@@ -0,0 +1,42 @@
+# Notes
+
+## Potential optimizations
+
+  * https://github.com/dotnet/runtime/blob/9b24fb60a19f62620ca1fc5e4eb2e3ae0b3b086d/src/coreclr/binder/assemblybindercommon.cpp#L844-L889
+    * Managed C++ assemblies aren't available on Unix, no point in looking for them
+    * `candidates[]` is `WCHAR*`, while `ProbeAppBundle` takes UTF-8 - no point in doing the
+      conversion here
+  * Host contract
+    * It might make sense to pass strings as Unicode to host and back, to avoid conversions.
+      p/invoke names for instance, can be Unicode. So can be the assembly names. Native library
+      names should be UTF-8, but we can generate lookup tables for those at application build time
+      (e.g. indexed by an xxHash of the Unicode version of the name) - no conversion at run time.
+    * We need declarations of all he possible HRESULT errors (`S_OK` etc)
+
+## Stuff that should be changed
+
+### Logging
+Currently, most of the messages logged by the runtime end up in `/dev/null` (either because they
+are disabled in release build or because they log to stdio which doesn't work on Android).
+
+Logcat is the only way to get information from remote devices, especially via Google Play Console.
+
+We should log to logcat:
+
+    + C++ exception messages
+    + abort() messages / fatal errors
+    + warnings
+    + errors
+
+A subsystem should be added which will provide a single function that will do actual output, implementation of which
+will be specific to the platform.  API should allow specification of severity, the actual message, and possibly a flag
+to indicate whether the process should be aborted (the decision might also be based on the severity).  Severity should
+be shared between all targets, which then can (if needed) translate it to the target platform's value(s), if any.
+
+### Process termination
+Runtime currently calls `abort()` in several places.  This should probably become part of the host contract instead.
+Being part of the contract, the target platform could implement process termination on `abort()` in a uniform way
+(includes platform-specific logging, preparation etc)
+
+## Issues and workarounds
+
diff --git a/Configuration.props b/Configuration.props
index 07a4740807e..2b4f189b5e5 100644
--- a/Configuration.props
+++ b/Configuration.props
@@ -220,4 +220,15 @@
     <AndroidAbiAndRuntimeFlavor Include="@(AndroidSupportedTargetJitAbi)" AndroidRuntime="NativeAOT" />
     <AndroidAbiAndRuntimeFlavor Include="@(AndroidSupportedTargetJitAbi)" AndroidRuntime="CoreCLR" />
   </ItemGroup>
+
+  <!-- Whenever there's a need to use a locally built CoreCLR, both .NET for Android and the
+       application(s) must be built with the same runtime. This property should point to the CoreCLR
+       artifact directory in its repository checkout:
+
+          {PATH_TO_DOTNET_RUNTIME_REPO}/artifacts/bin/microsoft.netcore.app.runtime.android-{ARCH}/{CONFIG}
+
+      <PropertyGroup>
+         <CLRLocalRuntimePath></CLRLocalRuntimePath>
+      </PropertyGroup>
+  -->
 </Project>
diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln
index 782b20c69db..9eb3895b885 100644
--- a/Xamarin.Android.sln
+++ b/Xamarin.Android.sln
@@ -45,7 +45,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.Diagnost
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.Cecil", "external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil.csproj", "{D48EE8D0-0A0A-4493-AEF5-DAF5F8CF86AD}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "native", "src\native\native-mono.csproj", "{53EE4C57-1C03-405A-8243-8DA539546C88}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "native-mono", "src\native\native-mono.csproj", "{53EE4C57-1C03-405A-8243-8DA539546C88}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}"
 EndProject
diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj
index 6d1820cff7c..af99678ba05 100644
--- a/build-tools/create-packs/Microsoft.Android.Runtime.proj
+++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj
@@ -60,7 +60,7 @@ projects that use the Microsoft.Android framework in .NET 6+.
       <_AndroidRuntimePackAssets Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libunwind_xamarin.a" />
     </ItemGroup>
 
-    <ItemGroup Condition=" '$(AndroidRuntime)' == 'CoreCLR' ">
+    <ItemGroup Condition=" '$(AndroidRuntime)' != 'NativeAOT' ">
       <!-- TODO: the Exists() checks must go away once we build CoreCLR host for all the targets -->
       <_AndroidRuntimePackAssets Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libc.so') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libc.so" />
       <_AndroidRuntimePackAssets Condition=" Exists('$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libdl.so') " Include="$(NativeRuntimeOutputRootDir)$(_RuntimeFlavorDirName)\$(AndroidRID)\libdl.so" />
diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets
index d7ed385f907..b7979948098 100644
--- a/build-tools/installers/create-installers.targets
+++ b/build-tools/installers/create-installers.targets
@@ -133,6 +133,10 @@
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_net6.jar" ExcludeFromLegacy="true" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_net6.dex" ExcludeFromLegacy="true" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_net6.dex" ExcludeFromLegacy="true" />
+    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_clr.jar" ExcludeFromLegacy="true" />
+    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_clr.jar" ExcludeFromLegacy="true" />
+    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_clr.dex" ExcludeFromLegacy="true" />
+    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_clr.dex" ExcludeFromLegacy="true" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)manifestmerger.jar" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)proguard-android.txt" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)protobuf-net.dll" />
@@ -175,18 +179,6 @@
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)K4os.Compression.LZ4.dll" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)ELFSharp.dll" />
     <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)ManifestOverlays\Timing.xml" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libc.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libm.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libc.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libm.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libc.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libm.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libc.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libm.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-arm64\libarchive-dso-stub.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-arm\libarchive-dso-stub.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-x64\libarchive-dso-stub.so" />
-    <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)dsostubs\android-x86\libarchive-dso-stub.so" />
   </ItemGroup>
   <ItemGroup>
     <_MSBuildTargetsSrcFiles Include="$(MSBuildTargetsSrcDir)\Xamarin.Android.AvailableItems.targets" />
diff --git a/build-tools/scripts/generate-pinvoke-tables.sh b/build-tools/scripts/generate-pinvoke-tables.sh
index 2b0f8083e37..dbf70afd352 100755
--- a/build-tools/scripts/generate-pinvoke-tables.sh
+++ b/build-tools/scripts/generate-pinvoke-tables.sh
@@ -2,11 +2,14 @@
 MY_DIR="$(dirname $0)"
 HOST="$(uname | tr A-Z a-z)"
 
-NATIVE_DIR="${MY_DIR}/../../src/native/mono"
-MONODROID_SOURCE_DIR="${NATIVE_DIR}/pinvoke-override"
-GENERATOR_SOURCE="${MONODROID_SOURCE_DIR}/generate-pinvoke-tables.cc"
-GENERATOR_BINARY="${MONODROID_SOURCE_DIR}/generate-pinvoke-tables"
-TARGET_FILE="${MONODROID_SOURCE_DIR}/pinvoke-tables.include"
+NATIVE_DIR="${MY_DIR}/../../src/native"
+MONODROID_SOURCE_DIR="${NATIVE_DIR}/mono/pinvoke-override"
+MONODROID_INCLUDE_DIR="${NATIVE_DIR}/mono/shared"
+CLR_SOURCE_DIR="${NATIVE_DIR}/clr/host"
+CLR_INCLUDE_DIR="${NATIVE_DIR}/clr/include/shared"
+GENERATOR_SOURCE="generate-pinvoke-tables.cc"
+GENERATOR_BINARY="generate-pinvoke-tables"
+TARGET_FILE="pinvoke-tables.include"
 GENERATED_FILE="${TARGET_FILE}.generated"
 DIFF_FILE="${TARGET_FILE}.diff"
 EXTERNAL_DIR="${MY_DIR}/../../external/"
@@ -64,33 +67,61 @@ case ${HOST} in
 	*) die Unsupported OS ;;
 esac
 
-${COMPILER} -O2 -std=c++20 -I${EXTERNAL_DIR} -I${EXTERNAL_DIR}/constexpr-xxh3 -I${NATIVE_DIR}/shared -I${NATIVE_DIR}/../common/include "${GENERATOR_SOURCE}" -o "${GENERATOR_BINARY}"
-"${GENERATOR_BINARY}" "${GENERATED_FILE}"
-
-FILES_DIFFER="no"
-cmp "${GENERATED_FILE}" "${TARGET_FILE}" > /dev/null 2>&1 || FILES_DIFFER="yes"
-
-RETVAL=0
-if [ "${TEST_ONLY}" == "no" ]; then
-	if [ "${FILES_DIFFER}" == "yes" ]; then
-		mv "${GENERATED_FILE}" "${TARGET_FILE}"
-	else
-		rm "${GENERATED_FILE}"
-	fi
-else
-	if [ "${FILES_DIFFER}" == "yes" ]; then
-		echo "Generated p/invokes table file differs from the current one"
-		diff -U3 -Narp "${TARGET_FILE}" "${GENERATED_FILE}" > "${DIFF_FILE}"
-
-		echo "Diff file saved in: ${DIFF_FILE}"
-		echo "------ DIFF START ------"
-		cat "${DIFF_FILE}"
-		echo "------ DIFF END ------"
-		echo
-		RETVAL=1
+function generate()
+{
+	local SOURCE_DIR="${1}"
+	local INCLUDE_DIR="${2}"
+	local SOURCE="${SOURCE_DIR}/${GENERATOR_SOURCE}"
+	local BINARY="${SOURCE_DIR}/${GENERATOR_BINARY}"
+	local RESULT="${SOURCE_DIR}/${GENERATED_FILE}"
+	local TARGET="${SOURCE_DIR}/${TARGET_FILE}"
+	local DIFF="${SOURCE_DIR}/${DIFF_FILE}"
+
+	${COMPILER} -O2 -std=c++20 -I${EXTERNAL_DIR} -I${EXTERNAL_DIR}/constexpr-xxh3 -I${INCLUDE_DIR} -I${NATIVE_DIR}/common/include "${SOURCE}" -o "${BINARY}"
+	"${BINARY}" "${RESULT}"
+
+	FILES_DIFFER="no"
+	cmp "${RESULT}" "${TARGET}" > /dev/null 2>&1 || FILES_DIFFER="yes"
+
+	if [ "${TEST_ONLY}" == "no" ]; then
+		if [ "${FILES_DIFFER}" == "yes" ]; then
+			  mv "${RESULT}" "${TARGET}"
+		else
+			rm "${RESULT}"
+		fi
 	else
-		echo Generated file is identical to the current one
+		if [ "${FILES_DIFFER}" == "yes" ]; then
+			echo "Generated p/invokes table file differs from the current one"
+			diff -U3 -Narp "${TARGET}" "${RESULT}" > "${DIFF}"
+
+			echo "Diff file saved in: ${DIFF}"
+			echo "------ DIFF START ------"
+			cat "${DIFF}"
+			echo "------ DIFF END ------"
+			echo
+			RETVAL=1
+		else
+			echo Generated file is identical to the current one
+		fi
 	fi
-fi
+}
+
+RETVAL=0
+cat <<EOF
+**
+** Generating for MonoVM
+**
+EOF
+generate "${MONODROID_SOURCE_DIR}" "${MONODROID_INCLUDE_DIR}"
+
+cat <<EOF
+
+--------------------------------------
+
+**
+** Generating for CoreCLR
+**
+EOF
+generate "${CLR_SOURCE_DIR}" "${CLR_INCLUDE_DIR}"
 
 exit ${RETVAL}
diff --git a/build-tools/scripts/xa_build_configuration.cmake.in b/build-tools/scripts/xa_build_configuration.cmake.in
index ad6da1b812a..8d350f17d27 100644
--- a/build-tools/scripts/xa_build_configuration.cmake.in
+++ b/build-tools/scripts/xa_build_configuration.cmake.in
@@ -2,3 +2,8 @@ set(NETCORE_APP_RUNTIME_DIR_ARM "@NETCORE_APP_RUNTIME_ANDROID_ARM@")
 set(NETCORE_APP_RUNTIME_DIR_ARM64 "@NETCORE_APP_RUNTIME_ANDROID_ARM64@")
 set(NETCORE_APP_RUNTIME_DIR_X86 "@NETCORE_APP_RUNTIME_ANDROID_X86@")
 set(NETCORE_APP_RUNTIME_DIR_X86_64 "@NETCORE_APP_RUNTIME_ANDROID_X86_64@")
+
+set(CORECLR_APP_RUNTIME_DIR_ARM "@CORECLR_APP_RUNTIME_ANDROID_ARM@")
+set(CORECLR_APP_RUNTIME_DIR_ARM64 "@CORECLR_APP_RUNTIME_ANDROID_ARM64@")
+set(CORECLR_APP_RUNTIME_DIR_X86 "@CORECLR_APP_RUNTIME_ANDROID_X86@")
+set(CORECLR_APP_RUNTIME_DIR_X86_64 "@CORECLR_APP_RUNTIME_ANDROID_X86_64@")
\ No newline at end of file
diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs
index 9c77341f969..9f49fb1e87b 100644
--- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs
+++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs
@@ -191,12 +191,18 @@ public static partial class Paths
 			public static string OpenJDKInstallDir                   => GetCachedPath (ref openJDKInstallDir, ()                   => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainDirectory), Defaults.JdkFolder));
 			public static string OpenJDKCacheDir                     => GetCachedPath (ref openJDKCacheDir, ()                     => ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory));
 
-			// .NET 6
+			// .NET 6+
 			public static string NetcoreAppRuntimeAndroidARM         => GetCachedPath (ref netcoreAppRuntimeAndroidARM, () => GetNetcoreAppRuntimePath (ctx, "arm"));
 			public static string NetcoreAppRuntimeAndroidARM64       => GetCachedPath (ref netcoreAppRuntimeAndroidARM64, () => GetNetcoreAppRuntimePath (ctx, "arm64"));
 			public static string NetcoreAppRuntimeAndroidX86         => GetCachedPath (ref netcoreAppRuntimeAndroidX86, () => GetNetcoreAppRuntimePath (ctx, "x86"));
 			public static string NetcoreAppRuntimeAndroidX86_64      => GetCachedPath (ref netcoreAppRuntimeAndroidX86_64, () => GetNetcoreAppRuntimePath (ctx, "x64"));
 
+			// CoreCLR
+			public static string CoreClrAppRuntimeAndroidARM         => GetCachedPath (ref coreclrAppRuntimeAndroidARM, () => GetCoreClrAppRuntimePath (ctx, "arm"));
+			public static string CoreClrAppRuntimeAndroidARM64       => GetCachedPath (ref coreclrAppRuntimeAndroidARM64, () => GetCoreClrAppRuntimePath (ctx, "arm64"));
+			public static string CoreClrAppRuntimeAndroidX86         => GetCachedPath (ref coreclrAppRuntimeAndroidX86, () => GetCoreClrAppRuntimePath (ctx, "x86"));
+			public static string CoreClrAppRuntimeAndroidX86_64      => GetCachedPath (ref coreclrAppRuntimeAndroidX86_64, () => GetCoreClrAppRuntimePath (ctx, "x64"));
+
 			public static string MicrosoftNETWorkloadMonoPackageDir => Path.Combine (
 				XAPackagesDir,
 				$"microsoft.net.workload.mono.toolchain.{{0}}.manifest-{ctx.Properties.GetRequiredValue (KnownProperties.DotNetMonoManifestVersionBand)}",
@@ -242,6 +248,17 @@ static string GetNetcoreAppRuntimePath (Context ctx, string androidTarget)
 				);
 			}
 
+			static string GetCoreClrAppRuntimePath (Context ctx, string androidTarget)
+			{
+				return Path.Combine (
+					XAPackagesDir,
+					$"microsoft.netcore.app.runtime.android-{androidTarget}",
+					ctx.Properties.GetRequiredValue (KnownProperties.MicrosoftNETCoreAppRefPackageVersion),
+					"runtimes",
+					$"android-{androidTarget}"
+				);
+			}
+
 			static string EnsureAndroidToolchainBinDirectories ()
 			{
 				if (androidToolchainBinDirectory != null)
@@ -278,6 +295,10 @@ static string GetCachedPath (ref string? variable, Func<string> creator)
 			static string? netcoreAppRuntimeAndroidARM64;
 			static string? netcoreAppRuntimeAndroidX86;
 			static string? netcoreAppRuntimeAndroidX86_64;
+			static string? coreclrAppRuntimeAndroidARM;
+			static string? coreclrAppRuntimeAndroidARM64;
+			static string? coreclrAppRuntimeAndroidX86;
+			static string? coreclrAppRuntimeAndroidX86_64;
 		}
 	}
 }
diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs
index af3c6cac1b6..65eddb2c042 100644
--- a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs
+++ b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs
@@ -98,6 +98,11 @@ GeneratedFile Get_Cmake_XA_Build_Configuration (Context context)
 				{ "@NETCORE_APP_RUNTIME_ANDROID_ARM64@",  Utilities.EscapePathSeparators (Configurables.Paths.NetcoreAppRuntimeAndroidARM64) },
 				{ "@NETCORE_APP_RUNTIME_ANDROID_X86@",    Utilities.EscapePathSeparators (Configurables.Paths.NetcoreAppRuntimeAndroidX86) },
 				{ "@NETCORE_APP_RUNTIME_ANDROID_X86_64@", Utilities.EscapePathSeparators (Configurables.Paths.NetcoreAppRuntimeAndroidX86_64) },
+
+				{ "@CORECLR_APP_RUNTIME_ANDROID_ARM@",    Utilities.EscapePathSeparators (Configurables.Paths.CoreClrAppRuntimeAndroidARM) },
+				{ "@CORECLR_APP_RUNTIME_ANDROID_ARM64@",  Utilities.EscapePathSeparators (Configurables.Paths.CoreClrAppRuntimeAndroidARM64) },
+				{ "@CORECLR_APP_RUNTIME_ANDROID_X86@",    Utilities.EscapePathSeparators (Configurables.Paths.CoreClrAppRuntimeAndroidX86) },
+				{ "@CORECLR_APP_RUNTIME_ANDROID_X86_64@", Utilities.EscapePathSeparators (Configurables.Paths.CoreClrAppRuntimeAndroidX86_64) },
 			};
 
 			return new GeneratedPlaceholdersFile (
@@ -107,7 +112,7 @@ GeneratedFile Get_Cmake_XA_Build_Configuration (Context context)
 			);
 		}
 
-		GeneratedFile Get_Cmake_Presets (Context context)
+		GeneratedFile GetCmakePresetsCommon (Context context, string sourcesDir)
 		{
 			const string OutputFileName = "CMakePresets.json";
 
@@ -126,11 +131,16 @@ GeneratedFile Get_Cmake_Presets (Context context)
 
 			return new GeneratedPlaceholdersFile (
 				replacements,
-				Path.Combine (Configurables.Paths.NativeSourcesDir, $"{OutputFileName}.in"),
-				Path.Combine (Configurables.Paths.NativeSourcesDir, OutputFileName)
+				Path.Combine (sourcesDir, $"{OutputFileName}.in"),
+				Path.Combine (sourcesDir, OutputFileName)
 			);
 		}
 
+		GeneratedFile Get_Cmake_Presets (Context context)
+		{
+			return GetCmakePresetsCommon (context, Configurables.Paths.NativeSourcesDir);
+		}
+
 		GeneratedFile Get_Configuration_Generated_Props (Context context)
 		{
 			const string OutputFileName = "Configuration.Generated.props";
diff --git a/build-tools/xaprepare/xaprepare/package-download.proj b/build-tools/xaprepare/xaprepare/package-download.proj
index f68692ad0b9..6f8053660df 100644
--- a/build-tools/xaprepare/xaprepare/package-download.proj
+++ b/build-tools/xaprepare/xaprepare/package-download.proj
@@ -20,6 +20,8 @@ Otherwise, $(MicrosoftNETCoreAppRefPackageVersion) from eng/Versions.props will
     <PackageDownload Include="Microsoft.NETCore.App.Runtime.Mono.android-arm64" Version="[$(DotNetRuntimePacksVersion)]" />
     <PackageDownload Include="Microsoft.NETCore.App.Runtime.Mono.android-x86" Version="[$(DotNetRuntimePacksVersion)]" />
     <PackageDownload Include="Microsoft.NETCore.App.Runtime.Mono.android-x64" Version="[$(DotNetRuntimePacksVersion)]" />
+    <PackageDownload Include="Microsoft.NETCore.App.Runtime.android-arm64" Version="[$(DotNetRuntimePacksVersion)]" />
+    <PackageDownload Include="Microsoft.NETCore.App.Runtime.android-x64" Version="[$(DotNetRuntimePacksVersion)]" />
     <PackageDownload Include="Microsoft.NET.Workload.Mono.ToolChain.Current.Manifest-$(DotNetMonoManifestVersionBand)" Version="[$(DotNetRuntimePacksVersion)]" />
     <PackageDownload Include="Microsoft.NET.Workload.Mono.ToolChain.net6.Manifest-$(DotNetMonoManifestVersionBand)" Version="[$(DotNetRuntimePacksVersion)]" />
     <PackageDownload Include="Microsoft.NET.Workload.Mono.ToolChain.net7.Manifest-$(DotNetMonoManifestVersionBand)" Version="[$(DotNetRuntimePacksVersion)]" />
diff --git a/src-ThirdParty/dotnet/runtime/README b/src-ThirdParty/dotnet/runtime/README
new file mode 100644
index 00000000000..c84cccac88f
--- /dev/null
+++ b/src-ThirdParty/dotnet/runtime/README
@@ -0,0 +1,11 @@
+The header files in this directory are verbatim copies taken from the
+https://github.com/dotnet/runtime/ repository. 
+
+They can be found in the following locations in the repository:
+
+  src/native/corehost/host_runtime_contract.h
+  src/coreclr/hosts/inc/coreclrhost.h
+
+and they MUST be in sync with the current version of the runtime being 
+used. They don't have to come from the same version, but their content 
+must be ABI and API compatible with it.
diff --git a/src-ThirdParty/dotnet/runtime/coreclrhost.h b/src-ThirdParty/dotnet/runtime/coreclrhost.h
new file mode 100644
index 00000000000..12099870c97
--- /dev/null
+++ b/src-ThirdParty/dotnet/runtime/coreclrhost.h
@@ -0,0 +1,158 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// APIs for hosting CoreCLR
+//
+
+#ifndef __CORECLR_HOST_H__
+#define __CORECLR_HOST_H__
+
+#if defined(_WIN32) && defined(_M_IX86)
+#define CORECLR_CALLING_CONVENTION __stdcall
+#else
+#define CORECLR_CALLING_CONVENTION
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+#define CORECLR_HOSTING_API_LINKAGE extern "C"
+#else
+#define CORECLR_HOSTING_API_LINKAGE
+#endif
+
+// For each hosting API, we define a function prototype and a function pointer
+// The prototype is useful for implicit linking against the dynamic coreclr
+// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary)
+#define CORECLR_HOSTING_API(function, ...) \
+    CORECLR_HOSTING_API_LINKAGE int CORECLR_CALLING_CONVENTION function(__VA_ARGS__); \
+    typedef int (CORECLR_CALLING_CONVENTION *function##_ptr)(__VA_ARGS__)
+
+//
+// Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain
+//
+// Parameters:
+//  exePath                 - Absolute path of the executable that invoked the ExecuteAssembly (the native host application)
+//  appDomainFriendlyName   - Friendly name of the app domain that will be created to execute the assembly
+//  propertyCount           - Number of properties (elements of the following two arguments)
+//  propertyKeys            - Keys of properties of the app domain
+//  propertyValues          - Values of properties of the app domain
+//  hostHandle              - Output parameter, handle of the created host
+//  domainId                - Output parameter, id of the created app domain
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_initialize,
+            const char* exePath,
+            const char* appDomainFriendlyName,
+            int propertyCount,
+            const char** propertyKeys,
+            const char** propertyValues,
+            void** hostHandle,
+            unsigned int* domainId);
+
+//
+// Type of the callback function that can be set by the coreclr_set_error_writer
+//
+typedef void (*coreclr_error_writer_callback_fn) (const char *message);
+
+//
+// Set callback for writing error logging
+//
+// Parameters:
+//  errorWriter             - callback that will be called for each line of the error info
+//                          - passing in NULL removes a callback that was previously set
+//
+// Returns:
+//  S_OK
+//
+CORECLR_HOSTING_API(coreclr_set_error_writer,
+            coreclr_error_writer_callback_fn errorWriter);
+
+//
+// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_shutdown,
+            void* hostHandle,
+            unsigned int domainId);
+
+//
+// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain
+//  latchedExitCode         - Latched exit code after domain unloaded
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_shutdown_2,
+            void* hostHandle,
+            unsigned int domainId,
+            int* latchedExitCode);
+
+//
+// Create a native callable function pointer for a managed method.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain
+//  entryPointAssemblyName  - Name of the assembly which holds the custom entry point
+//  entryPointTypeName      - Name of the type which holds the custom entry point
+//  entryPointMethodName    - Name of the method which is the custom entry point
+//  delegate                - Output parameter, the function stores a native callable function pointer to the delegate at the specified address
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_create_delegate,
+            void* hostHandle,
+            unsigned int domainId,
+            const char* entryPointAssemblyName,
+            const char* entryPointTypeName,
+            const char* entryPointMethodName,
+            void** delegate);
+
+//
+// Execute a managed assembly with given arguments
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain
+//  argc                    - Number of arguments passed to the executed assembly
+//  argv                    - Array of arguments passed to the executed assembly
+//  managedAssemblyPath     - Path of the managed assembly to execute (or NULL if using a custom entrypoint).
+//  exitCode                - Exit code returned by the executed assembly
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+CORECLR_HOSTING_API(coreclr_execute_assembly,
+            void* hostHandle,
+            unsigned int domainId,
+            int argc,
+            const char** argv,
+            const char* managedAssemblyPath,
+            unsigned int* exitCode);
+
+#undef CORECLR_HOSTING_API
+
+//
+// Callback types used by the hosts
+//
+typedef bool(CORECLR_CALLING_CONVENTION ExternalAssemblyProbeFn)(const char* path, void** data_start, int64_t* size);
+typedef bool(CORECLR_CALLING_CONVENTION BundleProbeFn)(const char* path, int64_t* offset, int64_t* size, int64_t* compressedSize);
+typedef const void* (CORECLR_CALLING_CONVENTION PInvokeOverrideFn)(const char* libraryName, const char* entrypointName);
+
+
+#endif // __CORECLR_HOST_H__
diff --git a/src-ThirdParty/dotnet/runtime/host_runtime_contract.h b/src-ThirdParty/dotnet/runtime/host_runtime_contract.h
new file mode 100644
index 00000000000..9d03c52cf24
--- /dev/null
+++ b/src-ThirdParty/dotnet/runtime/host_runtime_contract.h
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __HOST_RUNTIME_CONTRACT_H__
+#define __HOST_RUNTIME_CONTRACT_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32)
+    #define HOST_CONTRACT_CALLTYPE __stdcall
+#else
+    #define HOST_CONTRACT_CALLTYPE
+#endif
+
+// Known host property names
+#define HOST_PROPERTY_RUNTIME_CONTRACT "HOST_RUNTIME_CONTRACT"
+#define HOST_PROPERTY_APP_PATHS "APP_PATHS"
+#define HOST_PROPERTY_BUNDLE_PROBE "BUNDLE_PROBE"
+#define HOST_PROPERTY_ENTRY_ASSEMBLY_NAME "ENTRY_ASSEMBLY_NAME"
+#define HOST_PROPERTY_NATIVE_DLL_SEARCH_DIRECTORIES "NATIVE_DLL_SEARCH_DIRECTORIES"
+#define HOST_PROPERTY_PINVOKE_OVERRIDE "PINVOKE_OVERRIDE"
+#define HOST_PROPERTY_PLATFORM_RESOURCE_ROOTS "PLATFORM_RESOURCE_ROOTS"
+#define HOST_PROPERTY_TRUSTED_PLATFORM_ASSEMBLIES "TRUSTED_PLATFORM_ASSEMBLIES"
+
+struct host_runtime_contract
+{
+    size_t size;
+
+    // Context for the contract. Pass to functions taking a contract context.
+    void* context;
+
+    // Get the value of a runtime property.
+    // Returns the length of the property including a terminating null or -1 if not found.
+    size_t(HOST_CONTRACT_CALLTYPE* get_runtime_property)(
+        const char* key,
+        /*out*/ char* value_buffer,
+        size_t value_buffer_size,
+        void* contract_context);
+
+    // Probe an app bundle for `path`. Sets its location (`offset`, `size`) in the bundle if found.
+    // Returns true if found, false otherwise. If false, out parameter values are ignored by the runtime.
+    bool(HOST_CONTRACT_CALLTYPE* bundle_probe)(
+        const char* path,
+        /*out*/ int64_t* offset,
+        /*out*/ int64_t* size,
+        /*out*/ int64_t* compressedSize);
+
+    // Get the function overriding the specified p/invoke (`library_name`, `entry_point_name`).
+    // Returns a pointer to the function if the p/invoke is overridden, nullptr otherwise.
+    const void* (HOST_CONTRACT_CALLTYPE* pinvoke_override)(
+        const char* library_name,
+        const char* entry_point_name);
+
+    // Probe the host for `path`. Sets pointer to data start and its size, if found.
+    // Returns true if found, false otherwise. If false, out parameter values are ignored by the runtime.
+    bool(HOST_CONTRACT_CALLTYPE* external_assembly_probe)(
+        const char* path,
+        /*out*/ void **data_start,
+        /*out*/ int64_t* size);
+};
+
+#endif // __HOST_RUNTIME_CONTRACT_H__
diff --git a/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj b/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj
index 7612d7a2835..aac8b3bfe4c 100644
--- a/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj
+++ b/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj
@@ -52,6 +52,7 @@
   <ItemGroup>
     <Compile Include="$(IntermediateOutputPath)AssemblyInfo.cs" />
     <Compile Include="..\Mono.Android\Android.Runtime\AndroidRuntimeInternal.cs" />
+    <Compile Include="..\Mono.Android\Android.Runtime\DotNetRuntimeType.cs" />
     <Compile Include="..\Mono.Android\Android.Runtime\LogCategories.cs" />
     <Compile Include="..\Mono.Android\Android.Runtime\LogLevel.cs" />
     <Compile Include="..\Mono.Android\Android.Runtime\RuntimeConstants.cs" />
diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs b/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs
index 141ff50f122..31d80445485 100644
--- a/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs
+++ b/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs
@@ -4,14 +4,64 @@
 
 namespace Android.Runtime
 {
+	// The existence of InternalRuntimeTypeHolder and DotNetRuntimeTypeConverter classes looks weird, but
+	// we must handle a weird situation.  AndroidRuntimeInternal needs to know in its static constructor
+	// what is the current runtime type, but it cannot query JNIEnvInit.RuntimeType, since that type lives
+	// in the Mono.Android assembly, while AndroidRuntimeInternal lives in Mono.Android.Runtime and it cannot
+	// access JNIEnvInit and Mono.Android.Runtime doesn't reference Mono.Android but Mono.Android **does** reference
+	// Mono.Android.Runtime and has access to its internals.
+	//
+	// Mono.Android.Runtime, also, includes several source files from Mono.Android - **both** assemblies
+	// include the same source files.  In case of the DotNetRuntimeType enum, this declares two distinct types - one
+	// in Mono.Android and another in Mono.Android.Runtime, and so if JNIEnvInit.Initialize were to try to set the
+	// `DotNetRuntimeType RuntimeType;` field/property in either of the classes below, we'd get a compilation error
+	// to the effect of it being unable to cast `Android.Runtime.DotNetRuntimeType` to `Android.Runtime.DotNetRuntimeType`,
+	// which is usually as clear as mud :)
+	//
+	// To solve this and not duplicate code, the InternalRuntimeTypeHolder class is introduced which acts as a proxy since
+	// the AndroidRuntimeInternal static constructor must know the runtime type and JNIEnvInit.Initialize takes care of it by
+	// calling `SetRuntimeType` below long before AndroidRuntimeInternal cctor is invoked.
+	public static class InternalRuntimeTypeHolder
+	{
+		internal static DotNetRuntimeType RuntimeType = DotNetRuntimeType.Unknown;
+
+		internal static void SetRuntimeType (uint runtimeType)
+		{
+			RuntimeType = DotNetRuntimeTypeConverter.Convert (runtimeType);
+		}
+	}
+
 	public static class AndroidRuntimeInternal
 	{
-		internal static readonly Action<Exception> mono_unhandled_exception = RuntimeNativeMethods.monodroid_debugger_unhandled_exception;
+		internal static readonly Action<Exception> mono_unhandled_exception;
 
 #pragma warning disable CS0649 // Field is never assigned to.  This field is assigned from monodroid-glue.cc.
 		internal static volatile bool BridgeProcessing; // = false
 #pragma warning restore CS0649 // Field is never assigned to.
 
+		static AndroidRuntimeInternal ()
+		{
+			mono_unhandled_exception = InternalRuntimeTypeHolder.RuntimeType switch {
+				DotNetRuntimeType.MonoVM  => MonoUnhandledException,
+				DotNetRuntimeType.CoreCLR => CoreClrUnhandledException,
+				_                         => throw new NotSupportedException ($"Internal error: runtime type {InternalRuntimeTypeHolder.RuntimeType} not supported")
+			};
+		}
+
+		static void CoreClrUnhandledException (Exception ex)
+		{
+			// TODO: Is this even needed on CoreCLR?
+		}
+
+		// Needed when running under CoreCLR, which doesn't allow icalls/ecalls.  Any method which contains any reference to
+		// an unregistered icall/ecall method will fail to JIT (even if the method isn't actually called).  In this instance
+		// it affected the static constructor which tried to assign `RuntimeNativeMethods.monodroid_debugger_unhandled_exception`
+		// to `mono_unhandled_exception` at the top of the class.
+		static void MonoUnhandledException (Exception ex)
+		{
+			RuntimeNativeMethods.monodroid_debugger_unhandled_exception (ex);
+		}
+
 		public static void WaitForBridgeProcessing ()
 		{
 			if (!BridgeProcessing)
diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs
index 01fb5c03572..abea8497918 100644
--- a/src/Mono.Android/Android.Runtime/JNIEnv.cs
+++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs
@@ -77,6 +77,11 @@ internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMem
 			return wrap;
 		}
 
+		static void MonoDroidUnhandledException (Exception ex)
+		{
+			RuntimeNativeMethods.monodroid_unhandled_exception (ex);
+		}
+
 		internal static void PropagateUncaughtException (IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr)
 		{
 			if (!JNIEnvInit.PropagateExceptions)
@@ -95,7 +100,17 @@ internal static void PropagateUncaughtException (IntPtr env, IntPtr javaThreadPt
 				Logger.Log (LogLevel.Info, "MonoDroid", "UNHANDLED EXCEPTION:");
 				Logger.Log (LogLevel.Info, "MonoDroid", javaException.ToString ());
 
-				RuntimeNativeMethods.monodroid_unhandled_exception (innerException ?? javaException);
+				switch (JNIEnvInit.RuntimeType) {
+					case DotNetRuntimeType.MonoVM:
+						MonoDroidUnhandledException (innerException ?? javaException);
+						break;
+					case DotNetRuntimeType.CoreCLR:
+						// TODO: what to do here?
+						break;
+
+					default:
+						throw new NotSupportedException ($"Internal error: runtime type {JNIEnvInit.RuntimeType} not supported");
+				}
 			} catch (Exception e) {
 				Logger.Log (LogLevel.Error, "monodroid", "Exception thrown while raising AppDomain.UnhandledException event: " + e.ToString ());
 			}
@@ -414,6 +429,14 @@ internal static void LogTypemapTrace (StackTrace st)
 			}
 		}
 
+		// We need this proxy method because if `TypeManagedToJava` contained the call to `monodroid_typemap_managed_to_java`
+		// (which is an icall, or ecall in CoreCLR parlance), CoreCLR JIT would throw an exception, refusing to compile the
+		// method.  The exception would be thrown even if the icall weren't called (e.g. hidden behind a runtime type check)
+		static unsafe IntPtr monovm_typemap_managed_to_java (Type type, byte* mvidptr)
+		{
+			return monodroid_typemap_managed_to_java (type, mvidptr);
+		}
+
 		internal static unsafe string? TypemapManagedToJava (Type type)
 		{
 			if (mvid_bytes == null)
@@ -430,7 +453,11 @@ internal static void LogTypemapTrace (StackTrace st)
 
 			IntPtr ret;
 			fixed (byte* mvidptr = mvid_data) {
-				ret = monodroid_typemap_managed_to_java (type, mvidptr);
+				ret = JNIEnvInit.RuntimeType switch {
+					DotNetRuntimeType.MonoVM  => monovm_typemap_managed_to_java (type, mvidptr),
+					DotNetRuntimeType.CoreCLR => RuntimeNativeMethods.clr_typemap_managed_to_java (type.FullName, (IntPtr)mvidptr),
+					_                         => throw new NotSupportedException ($"Internal error: runtime type {JNIEnvInit.RuntimeType} not supported")
+				};
 			}
 
 			if (ret == IntPtr.Zero) {
diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
index 8c4ed9c5b7b..1e80e1f34b7 100644
--- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
+++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
@@ -90,7 +90,9 @@ internal static void InitializeJniRuntime (JniRuntime runtime)
 		[UnmanagedCallersOnly]
 		internal static unsafe void Initialize (JnienvInitializeArgs* args)
 		{
+			// This looks weird, see comments in RuntimeTypeInternal.cs
 			RuntimeType = DotNetRuntimeTypeConverter.Convert (args->runtimeType);
+			InternalRuntimeTypeHolder.SetRuntimeType (args->runtimeType);
 
 			IntPtr total_timing_sequence = IntPtr.Zero;
 			IntPtr partial_timing_sequence = IntPtr.Zero;
diff --git a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
index 8c3edcf1045..0c0963c84f7 100644
--- a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
+++ b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
@@ -6,7 +6,7 @@
 
 namespace Android.Runtime
 {
-	// Values must be identical to those in src/monodroid/jni/monodroid-glue-internal.hh
+	// NOTE: Keep this in sync with the native side in src/native/common/include/managed-interface.hh
 	[Flags]
 	enum TraceKind : uint
 	{
@@ -89,6 +89,12 @@ internal static class RuntimeNativeMethods
 		[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
 		internal static extern int _monodroid_max_gref_get ();
 
+		[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern IntPtr clr_typemap_managed_to_java (string fullName, IntPtr mvid);
+
+		[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+		internal static extern bool clr_typemap_java_to_managed (string java_type_name, out IntPtr managed_assembly_name, out uint managed_type_token_id);
+
 		[MethodImplAttribute(MethodImplOptions.InternalCall)]
 		internal static extern void monodroid_unhandled_exception (Exception javaException);
 
diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs
index f2e9a4bae7a..81ee5d6da47 100644
--- a/src/Mono.Android/Java.Interop/TypeManager.cs
+++ b/src/Mono.Android/Java.Interop/TypeManager.cs
@@ -224,9 +224,41 @@ static Exception CreateJavaLocationException ()
 		[MethodImplAttribute(MethodImplOptions.InternalCall)]
 		static extern Type monodroid_typemap_java_to_managed (string java_type_name);
 
+		static Type monovm_typemap_java_to_managed (string java_type_name)
+		{
+			return monodroid_typemap_java_to_managed (java_type_name);
+		}
+
+		[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Value of java_type_name isn't statically known.")]
+		static Type? clr_typemap_java_to_managed (string java_type_name)
+		{
+			bool result = RuntimeNativeMethods.clr_typemap_java_to_managed (java_type_name, out IntPtr managedAssemblyNamePointer, out uint managedTypeTokenId);
+			if (!result || managedAssemblyNamePointer == IntPtr.Zero) {
+				return null;
+			}
+
+			string managedAssemblyName = Marshal.PtrToStringAnsi (managedAssemblyNamePointer);
+			Assembly assembly = Assembly.Load (managedAssemblyName);
+			Type? ret = null;
+			foreach (Module module in assembly.Modules) {
+				ret = module.ResolveType ((int)managedTypeTokenId);
+				if (ret != null) {
+					break;
+				}
+			}
+
+			Logger.Log (LogLevel.Info, "monodroid", $"Loaded type: {ret}");
+			return ret;
+		}
+
 		internal static Type? GetJavaToManagedType (string class_name)
 		{
-			Type? type = monodroid_typemap_java_to_managed (class_name);
+			Type? type = JNIEnvInit.RuntimeType switch {
+				DotNetRuntimeType.MonoVM  => monovm_typemap_java_to_managed (class_name),
+				DotNetRuntimeType.CoreCLR => clr_typemap_java_to_managed (class_name),
+				_                         => throw new NotSupportedException ($"Internal error: runtime type {JNIEnvInit.RuntimeType} not supported")
+			};
+
 			if (type != null)
 				return type;
 
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
index 1ae80b94409..17d7009cc86 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
@@ -30,6 +30,7 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
   </ImportGroup>
 
   <UsingTask TaskName="Xamarin.Android.Tasks.GetAotAssemblies" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
+  <UsingTask TaskName="Xamarin.Android.Tasks.ProcessRuntimePackLibraryDirectories" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
 
   <Target Name="_AndroidAotInputs">
     <ItemGroup>
@@ -77,6 +78,17 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
     <ItemGroup>
       <AndroidAotProfile Include="$(MSBuildThisFileDirectory)dotnet.aotprofile" Condition=" '$(AndroidEnableProfiledAot)' == 'true' and '$(AndroidUseDefaultAotProfile)' != 'false' " />
     </ItemGroup>
+
+    <!-- We need to run this here in adddition to the _ResolveAssemblies target, because this code
+         runs in an inner build and `_RuntimePackLibraryDirectory` items aren't carried over to it.
+    -->
+    <ProcessRuntimePackLibraryDirectories
+        Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
+        ResolvedFilesToPublish="@(ResolvedFileToPublish)">
+      <Output TaskParameter="RuntimePackLibraryDirectories" ItemName="_RuntimePackLibraryDirectory" />
+      <Output TaskParameter="NativeLibrariesToRemove" ItemName="_NativeLibraryToRemove" />
+    </ProcessRuntimePackLibraryDirectories>
+
     <GetAotAssemblies
         AndroidAotMode="$(AndroidAotMode)"
         AndroidNdkDirectory="$(AndroidNdkDirectory)"
@@ -91,7 +103,8 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
         EnableLLVM="$(EnableLLVM)"
         Profiles="@(AndroidAotProfile)"
         StripLibraries="$(_AndroidAotStripLibraries)"
-        ZipAlignmentPages="$(AndroidZipAlignment)">
+        ZipAlignmentPages="$(AndroidZipAlignment)"
+        RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)">
       <Output PropertyName="_Triple"     TaskParameter="Triple" />
       <Output PropertyName="_ToolPrefix" TaskParameter="ToolPrefix" />
       <Output PropertyName="_MsymPath"   TaskParameter="MsymPath" />
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
index afda07d8c7d..54da968c370 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
@@ -12,6 +12,7 @@ _ResolveAssemblies MSBuild target.
 
   <UsingTask TaskName="Xamarin.Android.Tasks.ProcessAssemblies"      AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
   <UsingTask TaskName="Xamarin.Android.Tasks.ProcessNativeLibraries" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
+  <UsingTask TaskName="Xamarin.Android.Tasks.ProcessRuntimePackLibraryDirectories" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
   <UsingTask TaskName="Xamarin.Android.Tasks.StripNativeLibraries" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
 
   <!-- HACK: workaround for: https://github.com/dotnet/sdk/issues/25679 -->
@@ -105,6 +106,7 @@ _ResolveAssemblies MSBuild target.
     <ItemGroup>
       <_ProjectToBuild Include="$(MSBuildProjectFile)" AdditionalProperties="RuntimeIdentifier=%(_RIDs.Identity);$(_AdditionalProperties)" />
     </ItemGroup>
+
     <MSBuild
         Condition=" '$(DesignTimeBuild)' != 'true' "
         Projects="@(_ProjectToBuild)"
@@ -112,6 +114,22 @@ _ResolveAssemblies MSBuild target.
         Targets="_ComputeFilesToPublishForRuntimeIdentifiers">
       <Output TaskParameter="TargetOutputs" ItemName="ResolvedFileToPublish" />
     </MSBuild>
+
+    <!-- This must be done as early as possible, so the runtime libraries location is known to all
+         the tasks that need it and so that the shared library files that are never to be packaged
+         are never taken into consideration in any context.
+    -->
+    <ProcessRuntimePackLibraryDirectories
+        Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
+        ResolvedFilesToPublish="@(ResolvedFileToPublish)">
+      <Output TaskParameter="RuntimePackLibraryDirectories" ItemName="_RuntimePackLibraryDirectory" />
+      <Output TaskParameter="NativeLibrariesToRemove" ItemName="_NativeLibraryToRemove" />
+    </ProcessRuntimePackLibraryDirectories>
+
+    <ItemGroup>
+      <ResolvedFileToPublish Remove="@(_NativeLibraryToRemove)" />
+    </ItemGroup>
+
     <!-- Properties produced by the inner build in Microsoft.Android.Sdk.ILLink.targets -->
     <PropertyGroup>
       <_InnerIntermediateOutputPath Condition=" '$(RuntimeIdentifier)' == '' ">$(IntermediateOutputPath)%(_RIDs.Identity)\</_InnerIntermediateOutputPath>
@@ -125,10 +143,6 @@ _ResolveAssemblies MSBuild target.
       <_ResolvedJavaLibraries Include="@(ResolvedFileToPublish)" Condition=" '%(ResolvedFileToPublish.Extension)' == '.jar' " />
     </ItemGroup>
 
-    <ItemGroup>
-      <ResolvedFileToPublish Remove="@(_ResolvedArchiveDSOStub)" />
-    </ItemGroup>
-
     <!-- All assemblies must be per-RID, thus no `->Distinct()` on `InputAssemblies` or `ResolvedSymbols` items -->
     <ProcessAssemblies
         RuntimeIdentifiers="@(_RIDs)"
@@ -235,7 +249,12 @@ _ResolveAssemblies MSBuild target.
       <!-- Filename without extension -->
       <_ExcludedNativeLibraries Condition=" '$(_AndroidIncludeSystemGlobalizationNative)' != 'true' " Include="libSystem.Globalization.Native" />
       <_ExcludedNativeLibraries Condition=" '$(_AndroidEnableNativeStackTracing)' != 'true' " Include="libxamarin-native-tracing" />
+      <_ExcludedNativeLibraries Condition=" '$(_AndroidRuntime)' == 'MonoVM' Or '$(_AndroidRuntime)' == '' " Include="libnet-android.debug" />
+      <_ExcludedNativeLibraries Condition=" '$(_AndroidRuntime)' == 'MonoVM' Or '$(_AndroidRuntime)' == '' " Include="libnet-android.release" />
+      <_ExcludedNativeLibraries Condition=" '$(_AndroidRuntime)' == 'CoreCLR' " Include="libmono-android.debug" />
+      <_ExcludedNativeLibraries Condition=" '$(_AndroidRuntime)' == 'CoreCLR' " Include="libmono-android.release" />
     </ItemGroup>
+
     <ProcessNativeLibraries
         InputLibraries="@(_ResolvedNativeLibraries)"
         ExcludedLibraries="@(_ExcludedNativeLibraries)"
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets
index 82970b039f9..bef2c60a58f 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets
@@ -12,4 +12,17 @@ This file contains the CoreCLR-specific MSBuild logic for .NET for Android.
     <_AndroidRuntimePackRuntime>CoreCLR</_AndroidRuntimePackRuntime>
   </PropertyGroup>
 
+  <Target Name="_CLRUseLocalRuntimePacks" AfterTargets="ResolveFrameworkReferences"
+          Condition=" '$(_CLRLocalRuntimePath)' != '' And '$(_AndroidRuntime)' == 'CoreCLR' ">
+    <PropertyGroup>
+      <_DotNetRuntimeRepo>$(_CLRLocalRuntimePath)</_DotNetRuntimeRepo>
+      <_DotNetRuntimeConfiguration Condition=" '$(_DotNetRuntimeConfiguration)' == '' ">Release</_DotNetRuntimeConfiguration>
+    </PropertyGroup>
+    <ItemGroup>
+      <!-- update runtime pack to local build -->
+      <ResolvedRuntimePack
+          PackageDirectory="$(_DotnetRuntimeRepo)/artifacts/bin/microsoft.netcore.app.runtime.$(RuntimeIdentifier)/$(_DotNetRuntimeConfiguration)"
+          Condition=" '%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App' And Exists('$(_DotnetRuntimeRepo)/artifacts/bin/microsoft.netcore.app.runtime.$(RuntimeIdentifier)/$(_DotNetRuntimeConfiguration)') " />
+    </ItemGroup>
+  </Target>
 </Project>
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CollectNativeFilesForArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CollectNativeFilesForArchive.cs
index 78b64397882..4cf23bd43e3 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/CollectNativeFilesForArchive.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CollectNativeFilesForArchive.cs
@@ -53,6 +53,9 @@ public class CollectNativeFilesForArchive : AndroidTask
 	[Required]
 	public string AndroidBinUtilsDirectory { get; set; } = "";
 
+	[Required]
+	public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty<ITaskItem> ();
+
 	[Required]
 	public string IntermediateOutputPath { get; set; } = "";
 
@@ -68,7 +71,7 @@ public class CollectNativeFilesForArchive : AndroidTask
 	public override bool RunTask ()
 	{
 		var apk = new PackageFileListBuilder ();
-		var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath);
+		var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, RuntimePackLibraryDirectories, IntermediateOutputPath);
 
 		var outputFiles = new List<string> {
 			ApkOutputPath
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs
index 1aa86a4c907..2be75d0bb99 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CollectRuntimeConfigFilesForArchive.cs
@@ -19,6 +19,9 @@ public class CollectRuntimeConfigFilesForArchive : AndroidTask
 	[Required]
 	public string AndroidBinUtilsDirectory { get; set; } = "";
 
+	[Required]
+	public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty<ITaskItem> ();
+
 	[Required]
 	public string IntermediateOutputPath { get; set; } = "";
 
@@ -33,7 +36,7 @@ public class CollectRuntimeConfigFilesForArchive : AndroidTask
 	public override bool RunTask ()
 	{
 		var files = new PackageFileListBuilder ();
-		var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath);
+		var dsoWrapperConfig = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, RuntimePackLibraryDirectories, IntermediateOutputPath);
 
 		// We will place rc.bin in the `lib` directory next to the blob, to make startup slightly faster, as we will find the config file right after we encounter
 		// our assembly store.  Not only that, but also we'll be able to skip scanning the `base.apk` archive when split configs are enabled (which they are in 99%
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
index 806bb19a3fd..5beba41c2cc 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
@@ -317,10 +317,7 @@ void WriteTypeMappings (NativeCodeGenState state)
 				// NativeAOT typemaps are generated in `Microsoft.Android.Sdk.ILLink.TypeMappingStep`
 				return;
 			}
-			if (androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.CoreCLR) {
-				// TODO: CoreCLR typemaps will be emitted later
-				return;
-			}
+
 			Log.LogDebugMessage ($"Generating type maps for architecture '{state.TargetArch}'");
 			var tmg = new TypeMapGenerator (Log, state, androidRuntime);
 			if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, TypemapOutputDirectory, GenerateNativeAssembly)) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs
index 9603655aaf2..9e8f3e9ea94 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateMainAndroidManifest.cs
@@ -136,23 +136,23 @@ IList<string> MergeManifest (NativeCodeGenState codeGenState, Dictionary<string,
 
 	void GenerateAdditionalProviderSources (NativeCodeGenState codeGenState, IList<string> additionalProviders)
 	{
-		if (androidRuntime != Xamarin.Android.Tasks.AndroidRuntime.CoreCLR) {
-			// Create additional runtime provider java sources.
-			bool isMonoVM = androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.MonoVM;
-			string providerTemplateFile = isMonoVM ?
-				"MonoRuntimeProvider.Bundled.java" :
-				"NativeAotRuntimeProvider.java";
-			string providerTemplate = GetResource (providerTemplateFile);
-
-			foreach (var provider in additionalProviders) {
-				var contents = providerTemplate.Replace (isMonoVM ? "MonoRuntimeProvider" : "NativeAotRuntimeProvider", provider);
-				var real_provider = isMonoVM ?
-					Path.Combine (OutputDirectory, "src", "mono", provider + ".java") :
-					Path.Combine (OutputDirectory, "src", "net", "dot", "jni", "nativeaot", provider + ".java");
-				Files.CopyIfStringChanged (contents, real_provider);
-			}
-		} else {
-			Log.LogDebugMessage ($"Skipping android.content.ContentProvider generation for: {androidRuntime}");
+		// Create additional runtime provider java sources.
+		bool isMonoVM = androidRuntime switch {
+			Xamarin.Android.Tasks.AndroidRuntime.MonoVM => true,
+			Xamarin.Android.Tasks.AndroidRuntime.CoreCLR => true,
+			_ => false,
+		};
+		string providerTemplateFile = isMonoVM ?
+			"MonoRuntimeProvider.Bundled.java" :
+			"NativeAotRuntimeProvider.java";
+		string providerTemplate = GetResource (providerTemplateFile);
+
+		foreach (var provider in additionalProviders) {
+			var contents = providerTemplate.Replace (isMonoVM ? "MonoRuntimeProvider" : "NativeAotRuntimeProvider", provider);
+			var real_provider = isMonoVM ?
+				Path.Combine (OutputDirectory, "src", "mono", provider + ".java") :
+				Path.Combine (OutputDirectory, "src", "net", "dot", "jni", "nativeaot", provider + ".java");
+			Files.CopyIfStringChanged (contents, real_provider);
 		}
 
 		// For NativeAOT, generate JavaInteropRuntime.java
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
index d58e7f70e79..d3f039d6642 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
@@ -64,8 +64,12 @@ public class GeneratePackageManagerJava : AndroidTask
 		[Required]
 		public bool EnablePreloadAssembliesDefault { get; set; }
 
+		[Required]
+		public bool TargetsCLR { get; set; }
+
 		public bool EnableMarshalMethods { get; set; }
 		public string RuntimeConfigBinFilePath { get; set; }
+		public string ProjectRuntimeConfigFilePath { get; set; } = String.Empty;
 		public string BoundExceptionType { get; set; }
 
 		public string PackageNamingPolicy { get; set; }
@@ -319,31 +323,53 @@ void AddEnvironment ()
 
 			bool haveRuntimeConfigBlob = !String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath);
 			var jniRemappingNativeCodeInfo = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfo> (ProjectSpecificTaskObjectKey (GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfoKey), RegisteredTaskObjectLifetime.Build);
-			var appConfigAsmGen = new ApplicationConfigNativeAssemblyGenerator (environmentVariables, systemProperties, Log) {
-				UsesMonoAOT = usesMonoAOT,
-				UsesMonoLLVM = EnableLLVM,
-				UsesAssemblyPreload = environmentParser.UsesAssemblyPreload,
-				MonoAOTMode = aotMode.ToString ().ToLowerInvariant (),
-				AotEnableLazyLoad = AndroidAotEnableLazyLoad,
-				AndroidPackageName = AndroidPackageName,
-				BrokenExceptionTransitions = environmentParser.BrokenExceptionTransitions,
-				PackageNamingPolicy = pnp,
-				BoundExceptionType = boundExceptionType,
-				JniAddNativeMethodRegistrationAttributePresent = NativeCodeGenState.TemplateJniAddNativeMethodRegistrationAttributePresent,
-				HaveRuntimeConfigBlob = haveRuntimeConfigBlob,
-				NumberOfAssembliesInApk = assemblyCount,
-				BundledAssemblyNameWidth = assemblyNameWidth,
-				MonoComponents = (MonoComponent)monoComponents,
-				NativeLibraries = uniqueNativeLibraries,
-				HaveAssemblyStore = UseAssemblyStore,
-				AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token,
-				JNIEnvInitializeToken = jnienv_initialize_method_token,
-				JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token,
-				JniRemappingReplacementTypeCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementTypeCount,
-				JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount,
-				MarshalMethodsEnabled = EnableMarshalMethods,
-				IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (),
-			};
+			LLVMIR.LlvmIrComposer appConfigAsmGen;
+
+			if (TargetsCLR) {
+				Dictionary<string, string>? runtimeProperties = RuntimePropertiesParser.ParseConfig (ProjectRuntimeConfigFilePath);
+				appConfigAsmGen = new ApplicationConfigNativeAssemblyGeneratorCLR (environmentVariables, systemProperties, runtimeProperties, Log) {
+					UsesAssemblyPreload = environmentParser.UsesAssemblyPreload,
+					AndroidPackageName = AndroidPackageName,
+					PackageNamingPolicy = pnp,
+					JniAddNativeMethodRegistrationAttributePresent = NativeCodeGenState.TemplateJniAddNativeMethodRegistrationAttributePresent,
+					NumberOfAssembliesInApk = assemblyCount,
+					BundledAssemblyNameWidth = assemblyNameWidth,
+					NativeLibraries = uniqueNativeLibraries,
+					AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token,
+					JNIEnvInitializeToken = jnienv_initialize_method_token,
+					JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token,
+					JniRemappingReplacementTypeCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementTypeCount,
+					JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount,
+					MarshalMethodsEnabled = EnableMarshalMethods,
+					IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (),
+				};
+			} else {
+				appConfigAsmGen = new ApplicationConfigNativeAssemblyGenerator (environmentVariables, systemProperties, Log) {
+					UsesMonoAOT = usesMonoAOT,
+					UsesMonoLLVM = EnableLLVM,
+					UsesAssemblyPreload = environmentParser.UsesAssemblyPreload,
+					MonoAOTMode = aotMode.ToString ().ToLowerInvariant (),
+					AotEnableLazyLoad = AndroidAotEnableLazyLoad,
+					AndroidPackageName = AndroidPackageName,
+					BrokenExceptionTransitions = environmentParser.BrokenExceptionTransitions,
+					PackageNamingPolicy = pnp,
+					BoundExceptionType = boundExceptionType,
+					JniAddNativeMethodRegistrationAttributePresent = NativeCodeGenState.TemplateJniAddNativeMethodRegistrationAttributePresent,
+					HaveRuntimeConfigBlob = haveRuntimeConfigBlob,
+					NumberOfAssembliesInApk = assemblyCount,
+					BundledAssemblyNameWidth = assemblyNameWidth,
+					MonoComponents = (MonoComponent)monoComponents,
+					NativeLibraries = uniqueNativeLibraries,
+					HaveAssemblyStore = UseAssemblyStore,
+					AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token,
+					JNIEnvInitializeToken = jnienv_initialize_method_token,
+					JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token,
+					JniRemappingReplacementTypeCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementTypeCount,
+					JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount,
+					MarshalMethodsEnabled = EnableMarshalMethods,
+					IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (),
+				};
+			}
 			LLVMIR.LlvmIrModule appConfigModule = appConfigAsmGen.Construct ();
 
 			foreach (string abi in SupportedAbis) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs
index 749c1df612f..fce21988f58 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs
@@ -32,6 +32,9 @@ public abstract class GetAotArguments : AsyncTask
 		[Required]
 		public string TargetName { get; set; } = "";
 
+		[Required]
+		public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty<ITaskItem> ();
+
 		/// <summary>
 		/// Will be blank in .NET 6+
 		/// </summary>
@@ -295,10 +298,12 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP
 				libs.Add (Path.Combine (androidLibPath, "libc.so"));
 				libs.Add (Path.Combine (androidLibPath, "libm.so"));
 			} else if (!UseAndroidNdk && EnableLLVM) {
-				string libstubsPath = MonoAndroidHelper.GetLibstubsArchDirectoryPath (AndroidBinUtilsDirectory, arch);
+				string? libstubsPath = MonoAndroidHelper.GetRuntimePackNativeLibDir (arch, RuntimePackLibraryDirectories);
 
-				libs.Add (Path.Combine (libstubsPath, "libc.so"));
-				libs.Add (Path.Combine (libstubsPath, "libm.so"));
+				if (!String.IsNullOrEmpty (libstubsPath)) {
+					libs.Add (Path.Combine (libstubsPath, "libc.so"));
+					libs.Add (Path.Combine (libstubsPath, "libm.so"));
+				}
 			}
 
 			if (libs.Count > 0) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs
index 6fb2dac8967..5ac7faebc82 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs
@@ -43,6 +43,12 @@ sealed class InputFiles
 		[Required]
 		public string AndroidBinUtilsDirectory { get; set; }
 
+		[Required]
+		public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty<ITaskItem> ();
+
+		[Required]
+		public bool TargetsCLR { get; set; }
+
 		public int ZipAlignmentPages { get; set; } = AndroidZipAlign.DefaultZipAlignment64Bit;
 
 		public override System.Threading.Tasks.Task RunTaskAsync ()
@@ -114,20 +120,19 @@ void RunLinker (Config config)
 
 		IEnumerable<Config> GetLinkerConfigs ()
 		{
-			string runtimeNativeLibsDir = MonoAndroidHelper.GetNativeLibsRootDirectoryPath (AndroidBinUtilsDirectory);
-			string runtimeNativeLibStubsDir = MonoAndroidHelper.GetLibstubsRootDirectoryPath (AndroidBinUtilsDirectory);
 			var abis = new Dictionary <string, InputFiles> (StringComparer.Ordinal);
 			ITaskItem[] dsos = ApplicationSharedLibraries;
 			foreach (ITaskItem item in dsos) {
 				string abi = item.GetMetadata ("abi");
-				abis [abi] = GatherFilesForABI (item.ItemSpec, abi, ObjectFiles, runtimeNativeLibsDir, runtimeNativeLibStubsDir);
+				abis [abi] = GatherFilesForABI (item.ItemSpec, abi, ObjectFiles);
 			}
 
-			const string commonLinkerArgs =
+			string soname = TargetsCLR ? "libxamarin-app-clr.so" : "libxamarin-app.so";
+			string commonLinkerArgs =
 				"--shared " +
 				"--allow-shlib-undefined " +
 				"--export-dynamic " +
-				"-soname libxamarin-app.so " +
+				$"-soname {soname} " +
 				"-z relro " +
 				"-z noexecstack " +
 				"--enable-new-dtags " +
@@ -203,19 +208,21 @@ IEnumerable<Config> GetLinkerConfigs ()
 			}
 		}
 
-		InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem[] objectFiles, string runtimeNativeLibsDir, string runtimeNativeLibStubsDir)
+		InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem[] objectFiles)
 		{
-			List<string> extraLibraries = null;
+			AndroidTargetArch arch = MonoAndroidHelper.AbiToTargetArch (abi);
+			var libDirs = new HashSet<string> (StringComparer.OrdinalIgnoreCase) {
+				MonoAndroidHelper.GetRuntimePackNativeLibDir (arch, RuntimePackLibraryDirectories),
+			};
+
 			string RID = MonoAndroidHelper.AbiToRid (abi);
 			AndroidTargetArch targetArch = MonoAndroidHelper.AbiToTargetArch (abi);
-			string libStubsPath = Path.Combine (runtimeNativeLibStubsDir, RID);
-			string runtimeLibsDir = Path.Combine (runtimeNativeLibsDir, RID);
 
-			extraLibraries = new List<string> {
-				$"-L \"{runtimeLibsDir}\"",
-				$"-L \"{libStubsPath}\"",
-				"-lc",
-			};
+			var extraLibraries = new List<string> ();
+			foreach (string dir in libDirs) {
+				extraLibraries.Add ($"-L \"{dir}\"");
+			}
+			extraLibraries.Add ("-lc");
 
 			return new InputFiles {
 				OutputSharedLibrary = runtimeSharedLibrary,
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessAssemblies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessAssemblies.cs
index bce699d3883..884a94519e6 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessAssemblies.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessAssemblies.cs
@@ -36,6 +36,9 @@ public class ProcessAssemblies : AndroidTask
 
 		public ITaskItem [] InputJavaLibraries { get; set; } = Array.Empty<ITaskItem> ();
 
+		public string AndroidRuntime { get; set; } = String.Empty;
+		public string LocalClrDirectory { get; set; } = String.Empty;
+
 		[Output]
 		public ITaskItem []? OutputAssemblies { get; set; }
 
@@ -100,7 +103,8 @@ public override bool RunTask ()
 		void SetAssemblyAbiMetadata (string abi, ITaskItem assembly, ITaskItem? symbol)
 		{
 			if (String.IsNullOrEmpty (abi)) {
-				throw new ArgumentException ("must not be null or empty", nameof (abi));
+				string rid = assembly.GetMetadata ("RuntimeIdentifier") ?? "unknown";
+				throw new ArgumentException ($"must not be null or empty for assembly item '{assembly}' (RID '{rid}')", nameof (abi));
 			}
 
 			assembly.SetMetadata ("Abi", abi);
@@ -110,6 +114,13 @@ void SetAssemblyAbiMetadata (string abi, ITaskItem assembly, ITaskItem? symbol)
 		void SetAssemblyAbiMetadata (ITaskItem assembly, ITaskItem? symbol)
 		{
 			string rid = assembly.GetMetadata ("RuntimeIdentifier");
+			if (String.IsNullOrEmpty (rid)) {
+				throw new InvalidOperationException ($"Assembly '{assembly}' item doesn't have the required RuntimeIdentifier metadata");
+			}
+
+			if (!MonoAndroidHelper.IsValidRID (rid)) {
+				throw new InvalidOperationException ($"Assembly '{assembly}' item targets unsupported RuntimeIdentifier '{rid}'");
+			}
 
 			SetAssemblyAbiMetadata (AndroidRidAbiHelper.RuntimeIdentifierToAbi (rid), assembly, symbol);
 		}
@@ -165,7 +176,7 @@ void SetDestinationSubDirectory (ITaskItem assembly, ITaskItem? symbol)
 		{
 			string? rid = assembly.GetMetadata ("RuntimeIdentifier");
 			if (String.IsNullOrEmpty (rid)) {
-				throw new InvalidOperationException ($"Assembly '{assembly}' item is missing required ");
+				throw new InvalidOperationException ($"Assembly '{assembly}' item is missing required RuntimeIdentifier data");
 			}
 
 			string? abi = AndroidRidAbiHelper.RuntimeIdentifierToAbi (rid);
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs
index 4970875d659..d0c1263bc29 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs
@@ -59,11 +59,16 @@ public override bool RunTask ()
 					}
 					continue;
 				}
-				// Both libmono-android.debug.so and libmono-android.release.so are in InputLibraries.
+				// Both lib{mono,net}-android.debug.so and lib{mono,net}-android.release.so are in InputLibraries.
 				// Use IncludeDebugSymbols to determine which one to include.
-				// We may eventually have files such as `libmono-android-checked+asan.release.so` as well.
+				// We may eventually have files such as `lib{mono,net}-android-checked+asan.release.so` as well.
 				var fileName = Path.GetFileNameWithoutExtension (library.ItemSpec);
-				if (fileName.StartsWith ("libmono-android", StringComparison.Ordinal)) {
+				if (ExcludedLibraries != null && ExcludedLibraries.Contains (fileName, StringComparer.OrdinalIgnoreCase)) {
+					Log.LogDebugMessage ($"Excluding '{library.ItemSpec}'");
+					continue;
+				}
+
+				if (fileName.StartsWith ("libmono-android", StringComparison.Ordinal) || fileName.StartsWith ("libnet-android", StringComparison.Ordinal)) {
 					if (fileName.EndsWith (".debug", StringComparison.Ordinal)) {
 						if (!IncludeDebugSymbols)
 							continue;
@@ -73,6 +78,7 @@ public override bool RunTask ()
 							continue;
 						library.SetMetadata ("ArchiveFileName", "libmonodroid.so");
 					}
+					Log.LogDebugMessage ($"Including runtime: {library}");
 				} else if (DebugNativeLibraries.Contains (fileName)) {
 					if (!IncludeDebugSymbols) {
 						Log.LogDebugMessage ($"Excluding '{library.ItemSpec}' for release builds.");
@@ -82,9 +88,6 @@ public override bool RunTask ()
 					if (!wantedComponents.Contains (fileName)) {
 						continue;
 					}
-				} else if (ExcludedLibraries != null && ExcludedLibraries.Contains (fileName, StringComparer.OrdinalIgnoreCase)) {
-					Log.LogDebugMessage ($"Excluding '{library.ItemSpec}'");
-					continue;
 				}
 
 				output.Add (library);
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs
new file mode 100644
index 00000000000..04c1369c345
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessRuntimePackLibraryDirectories.cs
@@ -0,0 +1,101 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using Microsoft.Android.Build.Tasks;
+
+namespace Xamarin.Android.Tasks;
+
+public class ProcessRuntimePackLibraryDirectories : AndroidTask
+{
+	public override string TaskPrefix => "FRPLD";
+
+	static readonly HashSet<string> NativeLibraryNames = new (StringComparer.OrdinalIgnoreCase) {
+		"libarchive-dso-stub.so",
+		"libc.so",
+		"libdl.so",
+		"liblog.so",
+		"libm.so",
+		"libz.so",
+	};
+
+	[Required]
+	public ITaskItem[] ResolvedFilesToPublish { get; set; } = Array.Empty<ITaskItem> ();
+
+	[Output]
+	public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty<ITaskItem> ();
+
+	[Output]
+	public ITaskItem[] NativeLibrariesToRemove { get; set; }  = Array.Empty<ITaskItem> ();
+
+	public override bool RunTask ()
+	{
+		var libDirs = new List<ITaskItem> ();
+		var librariesToRemove = new List<ITaskItem> ();
+		var seenRIDs = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
+
+		foreach (ITaskItem item in ResolvedFilesToPublish) {
+			if (!IsInSupportedRuntimePack (item)) {
+				continue;
+			}
+
+			string? fileName = Path.GetFileName (item.ItemSpec);
+			if (String.IsNullOrEmpty (fileName) || !NativeLibraryNames.Contains (fileName)) {
+				continue;
+			}
+
+			string? rid = item.GetMetadata ("RuntimeIdentifier");
+			if (String.IsNullOrEmpty (rid)) {
+				Log.LogDebugMessage ($"Ignoring item '{item}' because it contains no runtime identifier metadata");
+				continue;
+			}
+
+			if (!seenRIDs.Contains (rid)) {
+				string? dirName = Path.GetDirectoryName (item.ItemSpec);
+				if (String.IsNullOrEmpty (dirName)) {
+					Log.LogDebugMessage ($"Item '{item}' path doesn't contain full file path");
+				} else {
+					libDirs.Add (MakeLibDirItem (item, dirName));
+				}
+				seenRIDs.Add (rid);
+				Log.LogDebugMessage ($"Discovered runtime pack library directory for '{rid}': {dirName}");
+			}
+
+			librariesToRemove.Add (item);
+			Log.LogDebugMessage ($"Item '{item}' will be removed from the set of native libraries to publish");
+		}
+
+		RuntimePackLibraryDirectories = libDirs.ToArray ();
+		NativeLibrariesToRemove = librariesToRemove.ToArray ();
+
+		return !Log.HasLoggedErrors;
+	}
+
+	ITaskItem MakeLibDirItem (ITaskItem sourceItem, string dir)
+	{
+		var ret = new TaskItem (dir);
+		sourceItem.CopyMetadataTo (ret);
+
+		// These make no sense for directories, remove just to be safe
+		ret.RemoveMetadata ("CopyLocal");
+		ret.RemoveMetadata ("CopyToPublishDirectory");
+		ret.RemoveMetadata ("DestinationSubPath");
+		ret.RemoveMetadata ("RelativePath");
+		return ret;
+	}
+
+	bool IsInSupportedRuntimePack (ITaskItem item)
+	{
+		string? NuGetPackageId = item.GetMetadata ("NuGetPackageId");
+		if (String.IsNullOrEmpty (NuGetPackageId)) {
+			return false;
+		}
+
+		return NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.CoreCLR.", StringComparison.OrdinalIgnoreCase) ||
+		       NuGetPackageId.StartsWith ("Microsoft.Android.Runtime.Mono.", StringComparison.OrdinalIgnoreCase);
+	}
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs
index ab297dd6d29..4a83aa4266e 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/WrapAssembliesAsSharedLibraries.cs
@@ -24,6 +24,9 @@ public class WrapAssembliesAsSharedLibraries : AndroidTask
 	[Required]
 	public string AndroidBinUtilsDirectory { get; set; } = "";
 
+	[Required]
+	public ITaskItem[] RuntimePackLibraryDirectories { get; set; } = Array.Empty<ITaskItem> ();
+
 	public bool IncludeDebugSymbols { get; set; }
 
 	[Required]
@@ -42,7 +45,7 @@ public class WrapAssembliesAsSharedLibraries : AndroidTask
 
 	public override bool RunTask ()
 	{
-		var wrapper_config = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, IntermediateOutputPath);
+		var wrapper_config = DSOWrapperGenerator.GetConfig (Log, AndroidBinUtilsDirectory, RuntimePackLibraryDirectories, IntermediateOutputPath);
 		var files = new PackageFileListBuilder ();
 
 		if (UseAssemblyStore)
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs
index 362ab093137..3d10afa3e33 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs
@@ -108,6 +108,9 @@ public void BuildBasicApplication ([Values (true, false)] bool isRelease, [Value
 		[Test]
 		public void BasicApplicationOtherRuntime ([Values (true, false)] bool isRelease)
 		{
+			// This test would fail, as it requires **our** updated runtime pack, which isn't currently created
+			// It is created in `src/native/native-clr.csproj` which isn't built atm.
+			Assert.Ignore ("CoreCLR support isn't fully enabled yet. This test will be enabled in a follow-up PR.");
 			var proj = new XamarinAndroidApplicationProject {
 				IsRelease = isRelease,
 				// Add locally downloaded CoreCLR packs
@@ -465,7 +468,7 @@ public void XA1037PropertyDeprecatedWarning (string property, string value, bool
 			XamarinAndroidProject proj = isBindingProject ? new XamarinAndroidBindingProject () : new XamarinAndroidApplicationProject ();
 			proj.IsRelease = isRelease;
 			proj.SetProperty (property, value);
-			
+
 			using (ProjectBuilder b = isBindingProject ? CreateDllBuilder (Path.Combine ("temp", TestName)) : CreateApkBuilder (Path.Combine ("temp", TestName))) {
 				Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
 				Assert.IsTrue (StringAssertEx.ContainsText (b.LastBuildOutput, $"The '{property}' MSBuild property is deprecated and will be removed"),
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
index 15801bf3dd3..3dd7b1863cb 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs
@@ -34,6 +34,8 @@ class DSOWrapperGenerator
 {
 	internal const string RegisteredConfigKey = ".:!DSOWrapperGeneratorConfig!:.";
 
+	public const string StubFileName = "libarchive-dso-stub.so";
+
 	internal class Config
 	{
 		public Dictionary<AndroidTargetArch, string> DSOStubPaths    { get; }
@@ -53,24 +55,22 @@ public Config (Dictionary<AndroidTargetArch, string> stubPaths, string androidBi
 	//
 	const ulong PayloadSectionAlignment = 0x4000;
 
-	public static Config GetConfig (TaskLoggingHelper log, string androidBinUtilsDirectory, string baseOutputDirectory)
+	public static Config GetConfig (TaskLoggingHelper log, string androidBinUtilsDirectory, ITaskItem[] runtimePackLibraryDirs, string baseOutputDirectory)
 	{
 		var stubPaths = new Dictionary<AndroidTargetArch, string> ();
-		string archiveDSOStubsRootDir = MonoAndroidHelper.GetDSOStubsRootDirectoryPath (androidBinUtilsDirectory);
 
-		foreach (string dir in Directory.EnumerateDirectories (archiveDSOStubsRootDir, "android-*")) {
-			string rid = Path.GetFileName (dir);
-			AndroidTargetArch arch = MonoAndroidHelper.RidToArchMaybe (rid);
-			if (arch == AndroidTargetArch.None) {
-				log.LogDebugMessage ($"Unable to extract a supported RID name from directory path '{dir}'");
+		foreach (ITaskItem packLibDir in runtimePackLibraryDirs) {
+			string ?packRID = packLibDir.GetMetadata ("RuntimeIdentifier");
+			if (String.IsNullOrEmpty (packRID)) {
 				continue;
 			}
 
-			string stubPath = Path.Combine (dir, "libarchive-dso-stub.so");
+			string stubPath = Path.Combine (packLibDir.ItemSpec, StubFileName);
 			if (!File.Exists (stubPath)) {
-				throw new InvalidOperationException ($"Internal error: archive DSO stub file '{stubPath}' does not exist");
+				throw new InvalidOperationException ($"Internal error: archive DSO stub file '{stubPath}' does not exist in runtime pack at {packLibDir}");
 			}
 
+			AndroidTargetArch arch = MonoAndroidHelper.RidToArch (packRID);
 			stubPaths.Add (arch, stubPath);
 		}
 
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
index f50e1444750..bd5f567cae6 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
@@ -569,28 +569,24 @@ static string GetToolsRootDirectoryRelativePath (string androidBinUtilsDirectory
 			return relPath;
 		}
 
-		public static string GetLibstubsArchDirectoryPath (string androidBinUtilsDirectory, AndroidTargetArch arch)
-		{
-			return Path.Combine (GetLibstubsRootDirectoryPath (androidBinUtilsDirectory), ArchToRid (arch));
-		}
-
-		public static string GetLibstubsRootDirectoryPath (string androidBinUtilsDirectory)
+#if MSBUILD
+		public static string? GetRuntimePackNativeLibDir (AndroidTargetArch arch, IEnumerable<ITaskItem> runtimePackLibDirs)
 		{
-			string relPath = GetToolsRootDirectoryRelativePath (androidBinUtilsDirectory);
-			return Path.GetFullPath (Path.Combine (androidBinUtilsDirectory, relPath, "libstubs"));
-		}
+			foreach (ITaskItem item in runtimePackLibDirs) {
+				string? rid = item.GetMetadata ("RuntimeIdentifier");
+				if (String.IsNullOrEmpty (rid)) {
+					continue;
+				}
 
-		public static string GetDSOStubsRootDirectoryPath (string androidBinUtilsDirectory)
-		{
-			string relPath = GetToolsRootDirectoryRelativePath (androidBinUtilsDirectory);
-			return Path.GetFullPath (Path.Combine (androidBinUtilsDirectory, relPath, "dsostubs"));
-		}
+				AndroidTargetArch itemArch = RidToArch (rid);
+				if (itemArch == arch) {
+					return item.ItemSpec;
+				}
+			}
 
-		public static string GetNativeLibsRootDirectoryPath (string androidBinUtilsDirectory)
-		{
-			string relPath = GetToolsRootDirectoryRelativePath (androidBinUtilsDirectory);
-			return Path.GetFullPath (Path.Combine (androidBinUtilsDirectory, relPath, "lib"));
+			return null;
 		}
+#endif // MSBUILD
 
 		public static string? GetAssemblyCulture (ITaskItem assembly)
 		{
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
index 32ee751b0c2..7a39539e258 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
@@ -448,7 +448,7 @@ bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, stri
 
 			LLVMIR.LlvmIrComposer composer = runtime switch {
 				AndroidRuntime.MonoVM => new TypeMappingReleaseNativeAssemblyGenerator (log, new NativeTypeMappingData (log, modules)),
-				AndroidRuntime.CoreCLR => throw new NotImplementedException ("CoreCLR support not implemented yet"),
+				AndroidRuntime.CoreCLR => new TypeMappingReleaseNativeAssemblyGeneratorCLR (log, new NativeTypeMappingData (log, modules)),
 				_ => throw new NotSupportedException ($"Internal error: unsupported runtime {runtime}")
 			};
 
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index 5061e088da5..d37b47e7c6f 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -305,6 +305,8 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
 	<_AndroidFastDeployEnvironmentFiles Condition=" '$(_AndroidFastDeployEnvironmentFiles)' == '' And '$(EmbedAssembliesIntoApk)' == 'False' ">True</_AndroidFastDeployEnvironmentFiles>
 	<_AndroidFastDeployEnvironmentFiles Condition=" '$(_AndroidFastDeployEnvironmentFiles)' == '' ">False</_AndroidFastDeployEnvironmentFiles>
 
+  <_AndroidUseCLR Condition=" '$(_AndroidRuntime)' == 'CoreCLR' ">True</_AndroidUseCLR>
+  <_AndroidUseCLR Condition=" '$(_AndroidRuntime)' != 'CoreCLR' ">False</_AndroidUseCLR>
 </PropertyGroup>
 
 <Choose>
@@ -339,6 +341,10 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
   <AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(_AndroidRuntime)' != 'NativeAOT' ">True</AndroidEnableMarshalMethods>
   <!-- NOTE: temporarily disable for NativeAOT for now, to get build passing -->
   <AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(_AndroidRuntime)' == 'NativeAOT' ">False</AndroidEnableMarshalMethods>
+
+  <!-- NOTE: temporarily disable for CoreCLR for now, until we have an implementation that works
+       with the new runtime -->
+  <AndroidEnableMarshalMethods Condition=" '$(AndroidEnableMarshalMethods)' == '' and '$(_AndroidRuntime)' == 'CoreCLR' ">False</AndroidEnableMarshalMethods>
   <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False</_AndroidUseMarshalMethods>
   <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods)</_AndroidUseMarshalMethods>
 </PropertyGroup>
@@ -1364,10 +1370,15 @@ because xbuild doesn't support framework reference assemblies.
 </Target>
 
 <Target Name="_CollectRuntimeJarFilenames">
-  <PropertyGroup>
+  <PropertyGroup Condition=" '$(_AndroidRuntime)' != 'CoreCLR' ">
     <_RuntimeJar>$(MSBuildThisFileDirectory)\java_runtime_net6.jar</_RuntimeJar>
     <_RuntimeDex>$(MSBuildThisFileDirectory)\java_runtime_net6.dex</_RuntimeDex>
   </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(_AndroidRuntime)' == 'CoreCLR' ">
+    <_RuntimeJar>$(MSBuildThisFileDirectory)\java_runtime_clr.jar</_RuntimeJar>
+    <_RuntimeDex>$(MSBuildThisFileDirectory)\java_runtime_clr.dex</_RuntimeDex>
+  </PropertyGroup>
 </Target>
 
 <Target Name="_GetMonoPlatformJarPath">
@@ -1388,7 +1399,7 @@ because xbuild doesn't support framework reference assemblies.
 		DependsOnTargets="_CollectRuntimeJarFilenames;$(_BeforeAddStaticResources);_GetMonoPlatformJarPath">
 	<CopyResource ResourceName="machine.config" OutputPath="$(MonoAndroidIntermediateAssemblyDir)machine.config" />
   <CopyResource
-      Condition=" '$(_AndroidRuntime)' == 'MonoVM' "
+      Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
       ResourceName="MonoRuntimeProvider.Bundled.java"
       OutputPath="$(_AndroidIntermediateJavaSourceDirectory)mono\MonoRuntimeProvider.java"
   />
@@ -1463,7 +1474,7 @@ because xbuild doesn't support framework reference assemblies.
 
 <!-- _PrepareAssemblies lives in Microsoft.Android.Sdk.AssemblyResolution.targets -->
 
-<Target Name="_PrepareNativeAssemblySources" Condition=" '$(_AndroidRuntime)' == 'MonoVM' ">
+<Target Name="_PrepareNativeAssemblySources" Condition=" '$(_AndroidRuntime)' != 'NativeAOT' ">
   <PrepareAbiItems
       BuildTargetAbis="@(_BuildTargetAbis)"
       NativeSourcesDir="$(_NativeAssemblySourceDir)"
@@ -1765,7 +1776,7 @@ because xbuild doesn't support framework reference assemblies.
 </Target>
 
 <Target Name="_GeneratePackageManagerJava"
-  Condition=" '$(_AndroidRuntime)' == 'MonoVM' "
+  Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
   DependsOnTargets="$(_GeneratePackageManagerJavaDependsOn)"
   Inputs="@(_GeneratePackageManagerJavaInputs)"
   Outputs="$(_AndroidStampDirectory)_GeneratePackageManagerJava.stamp">
@@ -1800,6 +1811,8 @@ because xbuild doesn't support framework reference assemblies.
     UseAssemblyStore="$(AndroidUseAssemblyStore)"
     EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
     CustomBundleConfigFile="$(AndroidBundleConfigurationFile)"
+    TargetsCLR="$(_AndroidUseCLR)"
+    ProjectRuntimeConfigFilePath="$(ProjectRuntimeConfigFilePath)"
   >
   </GeneratePackageManagerJava>
   <Touch Files="$(_AndroidStampDirectory)_GeneratePackageManagerJava.stamp" AlwaysCreate="True" />
@@ -1985,7 +1998,7 @@ because xbuild doesn't support framework reference assemblies.
 
   <!-- Shrink Mono.Android.dll by removing attribute only needed for GenerateJavaStubs -->
   <RemoveRegisterAttribute
-    Condition="'$(AndroidLinkMode)' != 'None' and '$(AndroidIncludeDebugSymbols)' != 'true' and '$(AndroidStripILAfterAOT)' != 'true' and '$(_AndroidRuntime)' == 'MonoVM' "
+    Condition="'$(AndroidLinkMode)' != 'None' and '$(AndroidIncludeDebugSymbols)' != 'true' and '$(AndroidStripILAfterAOT)' != 'true' and '$(_AndroidRuntime)' != 'NativeAOT' "
     ShrunkFrameworkAssemblies="@(_ShrunkAssemblies)" />
 
   <MakeDir Directories="$(MonoAndroidIntermediateAssemblyDir)shrunk" />
@@ -2061,7 +2074,7 @@ because xbuild doesn't support framework reference assemblies.
   </ItemGroup>
 </Target>
 
-<Target Name="_PrepareApplicationSharedLibraryItems" Condition=" '$(_AndroidRuntime)' == 'MonoVM' ">
+<Target Name="_PrepareApplicationSharedLibraryItems" Condition=" '$(_AndroidRuntime)' != 'NativeAOT' ">
   <ItemGroup>
     <_ApplicationSharedLibrary Include="$(_AndroidApplicationSharedLibraryPath)%(_BuildTargetAbis.Identity)\libxamarin-app.so">
       <abi>%(_BuildTargetAbis.Identity)</abi>
@@ -2079,6 +2092,8 @@ because xbuild doesn't support framework reference assemblies.
       DebugBuild="$(AndroidIncludeDebugSymbols)"
       AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
       ZipAlignmentPages="$(AndroidZipAlignment)"
+      TargetsCLR="$(_AndroidUseCLR)"
+      RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)"
   />
   <ItemGroup>
     <FileWrites Include="@(_ApplicationSharedLibrary)" />
@@ -2181,7 +2196,7 @@ because xbuild doesn't support framework reference assemblies.
   <PropertyGroup Condition=" '$(AndroidPackageFormat)' == 'aab' ">
     <_ApkOutputPath>$(_BaseZipIntermediate)</_ApkOutputPath>
   </PropertyGroup>
-  
+
   <!-- Put the assemblies and native libraries in the apk -->
   <!--
     NOTE: Adding Arguments to BuildApk or BuildBaseAppBundle
@@ -2189,6 +2204,7 @@ because xbuild doesn't support framework reference assemblies.
     in monodroid.
   -->
   <CreateAssemblyStore
+      Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
       AppSharedLibrariesDir="$(_AndroidApplicationSharedLibraryPath)"
       IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)"
       ResolvedFrameworkAssemblies="@(_BuildApkResolvedFrameworkAssemblies)"
@@ -2197,14 +2213,16 @@ because xbuild doesn't support framework reference assemblies.
       UseAssemblyStore="$(AndroidUseAssemblyStore)">
     <Output TaskParameter="AssembliesToAddToArchive" ItemName="_BuildApkAssembliesToAddToArchive" />
   </CreateAssemblyStore>
-    
+
   <WrapAssembliesAsSharedLibraries
+      Condition=" '$(_AndroidRuntime)' != 'NativeAOT' "
       AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
       IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)"
       IntermediateOutputPath="$(IntermediateOutputPath)"
       ResolvedAssemblies="@(_BuildApkAssembliesToAddToArchive)"
       SupportedAbis="@(_BuildTargetAbis)"
-      UseAssemblyStore="$(AndroidUseAssemblyStore)">
+      UseAssemblyStore="$(AndroidUseAssemblyStore)"
+      RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)">
     <Output TaskParameter="WrappedAssemblies" ItemName="FilesToAddToArchive" />
   </WrapAssembliesAsSharedLibraries>
 
@@ -2224,11 +2242,14 @@ because xbuild doesn't support framework reference assemblies.
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
   </CollectJarContentFilesForArchive>
 
+  <!-- CoreCLR builds embed runtime config in libxamarin-app.so -->
   <CollectRuntimeConfigFilesForArchive
+      Condition=" '$(_AndroidRuntime)' == 'MonoVM' "
       AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
       IntermediateOutputPath="$(IntermediateOutputPath)"
       RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
-      SupportedAbis="@(_BuildTargetAbis)">
+      SupportedAbis="@(_BuildTargetAbis)"
+      RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)">
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
   </CollectRuntimeConfigFilesForArchive>
 
@@ -2245,7 +2266,8 @@ because xbuild doesn't support framework reference assemblies.
       CheckedBuild="$(_AndroidCheckedBuild)"
       ZipAlignmentPages="$(AndroidZipAlignment)"
       AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
-      IntermediateOutputPath="$(IntermediateOutputPath)">
+      IntermediateOutputPath="$(IntermediateOutputPath)"
+      RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)">
     <Output TaskParameter="OutputFiles" ItemName="ApkFiles" />
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
     <Output TaskParameter="DSODirectoriesToDelete" ItemName="DSODirectoriesToDelete" />
@@ -2265,7 +2287,7 @@ because xbuild doesn't support framework reference assemblies.
   <!-- Hopefully this is temporary and doesn't actually need to be cleaned up. But for now let's not change existing behavior. -->
   <RemoveDir
       Directories="@(DSODirectoriesToDelete)" />
-  
+
   <BuildAppBundle
       Condition=" '$(AndroidPackageFormat)' == 'aab' "
       ToolPath="$(JavaToolPath)"
@@ -2332,7 +2354,7 @@ because xbuild doesn't support framework reference assemblies.
       TypeMappings="@(_AndroidTypeMaps)">
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
   </CollectTypeMapFilesForArchive>
-  
+
   <CollectJarContentFilesForArchive
       AndroidPackageFormat="$(AndroidPackageFormat)"
       ExcludeFiles="@(AndroidPackagingOptionsExclude)"
@@ -2343,11 +2365,14 @@ because xbuild doesn't support framework reference assemblies.
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
   </CollectJarContentFilesForArchive>
 
+  <!-- CoreCLR builds embed runtime config in libxamarin-app.so -->
   <CollectRuntimeConfigFilesForArchive
+      Condition=" '$(_AndroidRuntime)' == 'MonoVM' "
       AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
       IntermediateOutputPath="$(IntermediateOutputPath)"
       RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
-      SupportedAbis="@(_BuildTargetAbis)">
+      SupportedAbis="@(_BuildTargetAbis)"
+      RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)">
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
   </CollectRuntimeConfigFilesForArchive>
 
@@ -2363,7 +2388,8 @@ because xbuild doesn't support framework reference assemblies.
       IncludeWrapSh="$(AndroidIncludeWrapSh)"
       CheckedBuild="$(_AndroidCheckedBuild)"
       AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
-      IntermediateOutputPath="$(IntermediateOutputPath)">
+      IntermediateOutputPath="$(IntermediateOutputPath)"
+      RuntimePackLibraryDirectories="@(_RuntimePackLibraryDirectory)">
     <Output TaskParameter="OutputFiles" ItemName="ApkFiles" />
     <Output TaskParameter="FilesToAddToArchive" ItemName="FilesToAddToArchive" />
     <Output TaskParameter="DSODirectoriesToDelete" ItemName="DSODirectoriesToDelete" />
diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java
index 1c0487e046f..db90e2092ef 100644
--- a/src/java-runtime/java/mono/android/MonoPackageManager.java
+++ b/src/java-runtime/java/mono/android/MonoPackageManager.java
@@ -65,7 +65,8 @@ public static void LoadApplication (Context context)
 				}
 
 				//
-				// Should the order change here, src/monodroid/jni/SharedConstants.hh must be updated accordingly
+				// Should the order change here, src/mono/native/runtime-base/shared-constants.hh and
+				// src/native/clr/include/constants.hh must be updated accordingly
 				//
 				String[] appDirs = new String[] {filesDir, cacheDir, dataDir};
 				boolean haveSplitApks = runtimePackage.splitSourceDirs != null && runtimePackage.splitSourceDirs.length > 0;
diff --git a/src/native/.gitignore b/src/native/.gitignore
deleted file mode 100644
index 06af5ee0acd..00000000000
--- a/src/native/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-CMakeUserPresets.json
-CMakePresets.json
-static-analysis.*.txt
diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt
index a1cee1367a6..3c71fcf8580 100644
--- a/src/native/CMakeLists.txt
+++ b/src/native/CMakeLists.txt
@@ -155,10 +155,17 @@ include("${XA_BUILD_DIR}/xa_build_configuration.cmake")
 # Paths
 #
 if(IS_CLR_RUNTIME)
-  set(RUNTIME_DIR_ARM64  "${CORECLR_APP_RUNTIME_DIR_ARM64}")
-  set(RUNTIME_DIR_ARM    "${CORECLR_APP_RUNTIME_DIR_ARM}")
-  set(RUNTIME_DIR_X86_64 "${CORECLR_APP_RUNTIME_DIR_X86_64}")
-  set(RUNTIME_DIR_X86    "${CORECLR_APP_RUNTIME_DIR_X86}")
+  if(LOCAL_CORECLR_PATH)
+    set(RUNTIME_DIR_ARM64  "${LOCAL_CORECLR_PATH}/runtimes/android-arm64")
+    set(RUNTIME_DIR_ARM    "${LOCAL_CORECLR_PATH}/runtimes/android-arm")
+    set(RUNTIME_DIR_X86_64 "${LOCAL_CORECLR_PATH}/runtimes/android-x64")
+    set(RUNTIME_DIR_X86    "${LOCAL_CORECLR_PATH}/runtimes/android-x86")
+  else()
+    set(RUNTIME_DIR_ARM64  "${CORECLR_APP_RUNTIME_DIR_ARM64}")
+    set(RUNTIME_DIR_ARM    "${CORECLR_APP_RUNTIME_DIR_ARM}")
+    set(RUNTIME_DIR_X86_64 "${CORECLR_APP_RUNTIME_DIR_X86_64}")
+    set(RUNTIME_DIR_X86    "${CORECLR_APP_RUNTIME_DIR_X86}")
+  endif()
 
   # TEMPORARY: for now JI needs to build with MonoVM embedding APIs
   set(TEMP_NETCORE_RUNTIME_DIR_ARM64  "${NETCORE_APP_RUNTIME_DIR_ARM64}")
@@ -213,7 +220,13 @@ if(IS_MONO_RUNTIME)
 else()
   # TEMPORARY: for now JI needs to build with MonoVM embedding APIs
   set(TEMP_MONO_RUNTIME_INCLUDE_DIR ${TEMP_NETCORE_NET_RUNTIME_DIR}/native/include/mono-2.0)
-  set(RUNTIME_INCLUDE_DIR ${NET_RUNTIME_DIR}/native/include/clr)
+
+  if(LOCAL_CORECLR_PATH)
+    set(CLR_REPO_ROOT_PATH "${LOCAL_CORECLR_PATH}/../../../..")
+    set(RUNTIME_INCLUDE_DIR "${CLR_REPO_ROOT_PATH}/src/native/corehost;${CLR_REPO_ROOT_PATH}/src/coreclr/hosts/inc")
+  else()
+    set(RUNTIME_INCLUDE_DIR ${REPO_ROOT_DIR}/src-ThirdParty/dotnet/runtime)
+  endif()
 endif()
 
 set(JAVA_INTEROP_INCLUDE_DIR ${JAVA_INTEROP_SRC_PATH})
diff --git a/src/native/clr/host/CMakeLists.txt b/src/native/clr/host/CMakeLists.txt
new file mode 100644
index 00000000000..5e1c6098da6
--- /dev/null
+++ b/src/native/clr/host/CMakeLists.txt
@@ -0,0 +1,163 @@
+# Needed modules
+
+include(CheckIncludeFile)
+include(CheckCXXSymbolExists)
+
+set(BUILD_STATIC_LIBRARY OFF)
+
+if(DEBUG_BUILD)
+  # Convince NDK to really optimize our Debug builds. Without this, NDK's cmake toolchain definition
+  # will force a -O0 on us and our "debug" build is not really for debugging of our native code but
+  # rather for "debug" builds of user apps - it has extra code but it has to be as fast as possible.
+  set(XA_COMPILER_FLAGS_DEBUG "-fno-limit-debug-info -O2")
+  set(CMAKE_C_FLAGS_DEBUG ${XA_COMPILER_FLAGS_DEBUG})
+  set(CMAKE_CXX_FLAGS_DEBUG ${XA_COMPILER_FLAGS_DEBUG})
+elseif(NOT ANALYZERS_ENABLED)
+  set(BUILD_STATIC_LIBRARY OFF) # Turn off for now, until we implement dynamic runtime linking at app build time
+endif()
+
+# Library directories
+set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/lib/${ANDROID_RID}")
+set(XA_LIBRARY_STUBS_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/libstubs/${ANDROID_RID}")
+
+# Header checks
+
+# Sources
+string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_NET_ANDROID_SUFFIX)
+set(XAMARIN_NET_ANDROID_LIB "net-android${CHECKED_BUILD_INFIX}.${XAMARIN_NET_ANDROID_SUFFIX}")
+set(XAMARIN_NET_ANDROID_STATIC_LIB "${XAMARIN_NET_ANDROID_LIB}-static")
+
+set(XAMARIN_MONODROID_SOURCES
+  assembly-store.cc
+  host.cc
+  host-jni.cc
+  host-util.cc
+  internal-pinvokes.cc
+  os-bridge.cc
+  pinvoke-override.cc
+  typemap.cc
+)
+
+list(APPEND LOCAL_CLANG_CHECK_SOURCES
+  ${XAMARIN_MONODROID_SOURCES}
+)
+
+add_clang_check_sources("${LOCAL_CLANG_CHECK_SOURCES}")
+
+# Build
+add_library(
+  ${XAMARIN_NET_ANDROID_LIB}
+  SHARED ${XAMARIN_MONODROID_SOURCES}
+)
+
+if(BUILD_STATIC_LIBRARY)
+  add_library(
+    ${XAMARIN_NET_ANDROID_STATIC_LIB}
+    STATIC
+    ${XAMARIN_MONODROID_SOURCES}
+  )
+  set_static_library_suffix(${XAMARIN_NET_ANDROID_STATIC_LIB})
+endif()
+
+macro(lib_target_options TARGET_NAME)
+  target_compile_definitions(
+    ${TARGET_NAME}
+    PRIVATE
+    HAVE_CONFIG_H
+    HAVE_LZ4
+    JI_DLL_EXPORT
+    JI_NO_VISIBILITY
+  )
+
+  if(DONT_INLINE)
+    target_compile_definitions(
+      ${TARGET_NAME}
+      PRIVATE
+      NO_INLINE
+    )
+  endif()
+
+  if(DEBUG_BUILD AND NOT DISABLE_DEBUG)
+    target_compile_definitions(
+      ${TARGET_NAME}
+      PRIVATE
+      DEBUG
+    )
+  endif()
+
+  if (ENABLE_TIMING)
+    target_compile_definitions(
+      ${TARGET_NAME}
+      PRIVATE
+      MONODROID_TIMING
+    )
+  endif()
+
+  target_compile_options(
+    ${TARGET_NAME}
+    PRIVATE
+    ${XA_DEFAULT_SYMBOL_VISIBILITY}
+    ${XA_COMMON_CXX_ARGS}
+  )
+
+  target_include_directories(
+    ${TARGET_NAME} BEFORE
+    PRIVATE
+    ${CMAKE_CURRENT_BINARY_DIR}/include
+    ${EXTERNAL_DIR}
+    ${ROBIN_MAP_DIR}/include
+  )
+
+  target_include_directories(
+    ${TARGET_NAME}
+    SYSTEM PRIVATE
+    ${SYSROOT_CXX_INCLUDE_DIR}
+    ${RUNTIME_INCLUDE_DIR}
+    ${TEMP_MONO_RUNTIME_INCLUDE_DIR}
+    ${NATIVE_TRACING_INCLUDE_DIRS}
+    ${LIBUNWIND_INCLUDE_DIRS}
+  )
+
+  target_link_directories(
+    ${TARGET_NAME}
+    PRIVATE
+    ${NET_RUNTIME_DIR}/native
+  )
+
+  target_link_options(
+    ${TARGET_NAME}
+    PRIVATE
+    ${XA_DEFAULT_SYMBOL_VISIBILITY}
+    ${XA_COMMON_CXX_LINKER_ARGS}
+    ${XA_CXX_DSO_LINKER_ARGS}
+  )
+
+  target_link_libraries(
+    ${TARGET_NAME}
+    xa::xamarin-app
+    ${SHARED_LIB_NAME}
+    xa::xamarin-startup
+    xa::runtime-base
+    xa::java-interop
+#    xa::pinvoke-override-precompiled
+    xa::lz4
+    -llog
+    -lcoreclr
+  )
+endmacro ()
+
+lib_target_options(${XAMARIN_NET_ANDROID_LIB})
+xa_add_compile_definitions(${XAMARIN_NET_ANDROID_LIB})
+xa_add_include_directories(${XAMARIN_NET_ANDROID_LIB})
+
+target_link_options(${XAMARIN_NET_ANDROID_LIB}
+  PRIVATE
+  -Wl,--version-script,${CMAKE_SOURCE_DIR}/clr/libnet-android.map.txt
+  -Wl,--no-undefined-version
+)
+
+if(BUILD_STATIC_LIBRARY)
+  lib_target_options(${XAMARIN_NET_ANDROID_STATIC_LIB})
+  xa_add_compile_definitions(${XAMARIN_NET_ANDROID_STATIC_LIB})
+  xa_add_include_directories(${XAMARIN_NET_ANDROID_STATIC_LIB})
+endif()
diff --git a/src/native/clr/host/assembly-store.cc b/src/native/clr/host/assembly-store.cc
new file mode 100644
index 00000000000..75ca4471dfd
--- /dev/null
+++ b/src/native/clr/host/assembly-store.cc
@@ -0,0 +1,250 @@
+#include <string>
+
+#if defined (HAVE_LZ4)
+#include <lz4.h>
+#endif
+
+#include <xamarin-app.hh>
+#include <host/assembly-store.hh>
+#include <runtime-base/util.hh>
+#include <runtime-base/search.hh>
+#include <runtime-base/startup-aware-lock.hh>
+
+using namespace xamarin::android;
+
+[[gnu::always_inline]]
+void AssemblyStore::set_assembly_data_and_size (uint8_t* source_assembly_data, uint32_t source_assembly_data_size, uint8_t*& dest_assembly_data, uint32_t& dest_assembly_data_size) noexcept
+{
+	dest_assembly_data = source_assembly_data;
+	dest_assembly_data_size = source_assembly_data_size;
+}
+
+[[gnu::always_inline]]
+auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData const& e, std::string_view const& name, bool force_rw) noexcept -> std::tuple<uint8_t*, uint32_t>
+{
+	uint8_t *assembly_data = nullptr;
+	uint32_t assembly_data_size = 0;
+
+#if defined (HAVE_LZ4) && defined (RELEASE)
+	auto header = reinterpret_cast<const CompressedAssemblyHeader*>(e.image_data);
+	if (header->magic == COMPRESSED_DATA_MAGIC) {
+		log_debug (LOG_ASSEMBLY, "Decompressing assembly '{}' from the assembly store", name);
+		if (compressed_assemblies.descriptors == nullptr) [[unlikely]] {
+			Helpers::abort_application (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined"sv);
+		}
+		if (header->descriptor_index >= compressed_assemblies.count) [[unlikely]] {
+			Helpers::abort_application (
+				LOG_ASSEMBLY,
+				std::format (
+					"Invalid compressed assembly descriptor index {}",
+					header->descriptor_index
+				)
+			);
+		}
+
+		CompressedAssemblyDescriptor &cad = compressed_assemblies.descriptors[header->descriptor_index];
+		assembly_data_size = e.descriptor->data_size - sizeof(CompressedAssemblyHeader);
+		if (!cad.loaded) {
+			StartupAwareLock decompress_lock (assembly_decompress_mutex);
+
+			if (cad.loaded) {
+				set_assembly_data_and_size (reinterpret_cast<uint8_t*>(cad.data), cad.uncompressed_file_size, assembly_data, assembly_data_size);
+				return {assembly_data, assembly_data_size};
+			}
+
+			if (cad.data == nullptr) [[unlikely]] {
+				Helpers::abort_application (
+					LOG_ASSEMBLY,
+					std::format (
+						"Invalid compressed assembly descriptor at {}: no data",
+						header->descriptor_index
+					)
+				);
+			}
+
+			if (header->uncompressed_length != cad.uncompressed_file_size) {
+				if (header->uncompressed_length > cad.uncompressed_file_size) {
+					Helpers::abort_application (
+						LOG_ASSEMBLY,
+						std::format (
+							"Compressed assembly '{}' is larger than when the application was built (expected at most {}, got {}). Assemblies don't grow just like that!",
+							name,
+							cad.uncompressed_file_size,
+							header->uncompressed_length
+						)
+					);
+				} else {
+					log_debug (LOG_ASSEMBLY, "Compressed assembly '{}' is smaller than when the application was built. Adjusting accordingly.", name);
+				}
+				cad.uncompressed_file_size = header->uncompressed_length;
+			}
+
+			const char *data_start = pointer_add<const char*>(e.image_data, sizeof(CompressedAssemblyHeader));
+			int ret = LZ4_decompress_safe (data_start, reinterpret_cast<char*>(cad.data), static_cast<int>(assembly_data_size), static_cast<int>(cad.uncompressed_file_size));
+
+			if (ret < 0) {
+				Helpers::abort_application (
+					LOG_ASSEMBLY,
+					std::format (
+						"Decompression of assembly {} failed with code {}",
+						name,
+						ret
+					)
+				);
+			}
+
+			if (static_cast<uint64_t>(ret) != cad.uncompressed_file_size) {
+				Helpers::abort_application (
+					LOG_ASSEMBLY,
+					std::format (
+						"Decompression of assembly {} yielded a different size (expected {}, got {})",
+						name,
+						cad.uncompressed_file_size,
+						static_cast<uint32_t>(ret)
+					)
+				);
+			}
+			cad.loaded = true;
+		}
+
+		set_assembly_data_and_size (reinterpret_cast<uint8_t*>(cad.data), cad.uncompressed_file_size, assembly_data, assembly_data_size);
+	} else
+#endif // def HAVE_LZ4 && def RELEASE
+	{
+		log_debug (LOG_ASSEMBLY, "Assembly '{}' is not compressed in the assembly store", name);
+
+		// HACK! START
+		// Currently, MAUI crashes when we return a pointer to read-only data, so we must copy
+		// the assembly data to a read-write area.
+		log_debug (LOG_ASSEMBLY, "Copying assembly data to an r/w memory area");
+		uint8_t *rw_pointer = static_cast<uint8_t*>(malloc (e.descriptor->data_size));
+		memcpy (rw_pointer, e.image_data, e.descriptor->data_size);
+		set_assembly_data_and_size (rw_pointer, e.descriptor->data_size, assembly_data, assembly_data_size);
+		// HACK! END
+		// 	set_assembly_data_and_size (e.image_data, e.descriptor->data_size, assembly_data, assembly_data_size);
+	}
+
+	return {assembly_data, assembly_data_size};
+}
+
+[[gnu::always_inline]]
+auto AssemblyStore::find_assembly_store_entry (hash_t hash, const AssemblyStoreIndexEntry *entries, size_t entry_count) noexcept -> const AssemblyStoreIndexEntry*
+{
+	auto equal = [](AssemblyStoreIndexEntry const& entry, hash_t key) -> bool { return entry.name_hash == key; };
+	auto less_than = [](AssemblyStoreIndexEntry const& entry, hash_t key) -> bool { return entry.name_hash < key; };
+	ssize_t idx = Search::binary_search<AssemblyStoreIndexEntry, equal, less_than> (hash, entries, entry_count);
+	if (idx >= 0) {
+		return &entries[idx];
+	}
+	return nullptr;
+}
+
+auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size) noexcept -> void*
+{
+	hash_t name_hash = xxhash::hash (name.data (), name.length ());
+	log_debug (LOG_ASSEMBLY, "assembly_store_open_from_bundles: looking for bundled name: '{}' (hash {:x})", optional_string (name.data ()), name_hash);
+
+	const AssemblyStoreIndexEntry *hash_entry = find_assembly_store_entry (name_hash, assembly_store_hashes, assembly_store.index_entry_count);
+	if (hash_entry == nullptr) {
+		log_warn (LOG_ASSEMBLY, "Assembly '{}' (hash 0x{:x}) not found", optional_string (name.data ()), name_hash);
+		return nullptr;
+	}
+
+	if (hash_entry->descriptor_index >= assembly_store.assembly_count) {
+		Helpers::abort_application (
+			LOG_ASSEMBLY,
+			std::format (
+				"Invalid assembly descriptor index {}, exceeds the maximum value of {}",
+				hash_entry->descriptor_index,
+				assembly_store.assembly_count - 1
+			)
+		);
+	}
+
+	AssemblyStoreEntryDescriptor &store_entry = assembly_store.assemblies[hash_entry->descriptor_index];
+	AssemblyStoreSingleAssemblyRuntimeData &assembly_runtime_info = assembly_store_bundled_assemblies[store_entry.mapping_index];
+
+	if (assembly_runtime_info.image_data == nullptr) {
+		// The assignments here don't need to be atomic, the value will always be the same, so even if two threads
+		// arrive here at the same time, nothing bad will happen.
+		assembly_runtime_info.image_data = assembly_store.data_start + store_entry.data_offset;
+		assembly_runtime_info.descriptor = &store_entry;
+		if (store_entry.debug_data_offset != 0) {
+			assembly_runtime_info.debug_info_data = assembly_store.data_start + store_entry.debug_data_offset;
+		}
+
+		log_debug (
+			LOG_ASSEMBLY,
+			"Mapped: image_data == {:p}; debug_info_data == {:p}; config_data == {:p}; descriptor == {:p}; data size == {}; debug data size == {}; config data size == {}; name == '{}'",
+			static_cast<void*>(assembly_runtime_info.image_data),
+			static_cast<void*>(assembly_runtime_info.debug_info_data),
+			static_cast<void*>(assembly_runtime_info.config_data),
+			static_cast<void*>(assembly_runtime_info.descriptor),
+			assembly_runtime_info.descriptor->data_size,
+			assembly_runtime_info.descriptor->debug_data_size,
+			assembly_runtime_info.descriptor->config_data_size,
+			name
+		);
+	}
+
+	constexpr hash_t mscorlib_hash = 0x579a06fed6eec900;
+	auto [assembly_data, assembly_data_size] = get_assembly_data (assembly_runtime_info, name, name_hash == mscorlib_hash);
+	size = assembly_data_size;
+	return assembly_data;
+}
+
+void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_view const& store_path, uint32_t offset, uint32_t size) noexcept
+{
+	detail::mmap_info assembly_store_map = Util::mmap_file (fd, offset, size, store_path);
+
+	auto [payload_start, payload_size] = Util::get_wrapper_dso_payload_pointer_and_size (assembly_store_map, store_path);
+	log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: {:p}; size: {}", payload_start, payload_size);
+	auto header = static_cast<AssemblyStoreHeader*>(payload_start);
+
+	auto get_full_store_path = [&apk_path, &store_path]() -> std::string {
+		std::string full_store_path;
+
+		if (!apk_path.empty ()) {
+			full_store_path.append (apk_path);
+			// store path will be relative, to the apk
+			full_store_path.append ("!/"sv);
+			full_store_path.append (store_path);
+		} else {
+			full_store_path.append (store_path);
+		}
+
+		return full_store_path;
+	};
+
+	if (header->magic != ASSEMBLY_STORE_MAGIC) {
+		Helpers::abort_application (
+			LOG_ASSEMBLY,
+			std::format (
+				"Assembly store '{}' is not a valid .NET for Android assembly store file",
+				get_full_store_path ()
+			)
+		);
+	}
+
+	if (header->version != ASSEMBLY_STORE_FORMAT_VERSION) {
+		Helpers::abort_application (
+			LOG_ASSEMBLY,
+			std::format (
+				"Assembly store '{}' uses format version {:x}, instead of the expected {:x}",
+				get_full_store_path (),
+				header->version,
+				ASSEMBLY_STORE_FORMAT_VERSION
+			)
+		);
+	}
+
+	constexpr size_t header_size = sizeof(AssemblyStoreHeader);
+
+	assembly_store.data_start = static_cast<uint8_t*>(payload_start);
+	assembly_store.assembly_count = header->entry_count;
+	assembly_store.index_entry_count = header->index_entry_count;
+	assembly_store.assemblies = reinterpret_cast<AssemblyStoreEntryDescriptor*>(assembly_store.data_start + header_size + header->index_size);
+	assembly_store_hashes = reinterpret_cast<AssemblyStoreIndexEntry*>(assembly_store.data_start + header_size);
+
+	log_debug (LOG_ASSEMBLY, "Mapped assembly store {}", get_full_store_path ());
+}
diff --git a/src/native/clr/host/generate-pinvoke-tables.cc b/src/native/clr/host/generate-pinvoke-tables.cc
new file mode 100644
index 00000000000..e2055f45763
--- /dev/null
+++ b/src/native/clr/host/generate-pinvoke-tables.cc
@@ -0,0 +1,753 @@
+//
+// To build and run this utility run (on Linux or macOS):
+//
+//   ../../../../build-tools/scripts/generate-pinvoke-tables.sh
+//
+// A reasonable C++20 compiler is required (g++ 10+, clang 11+, on mac it may require XCode 12.5 or newer)
+//
+// Whenever a new p/invoke (or entire new shared libary which is part of dotnet distribution) is added, try to keep the
+// entries sorted alphabetically.  This is not required by the generator but easier to examine by humans.
+//
+// If a new library is added, please remember to generate a hash of its name and update pinvoke-override-api.cc
+//
+// To get the list of exported native symbols for a library, you can run the following command on Unix:
+//
+//   for s in $(llvm-nm -DUj [LIBRARY] | sort); do echo "\"$s\","; done
+//
+#include <algorithm>
+#include <cerrno>
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <shared/xxhash.hh>
+
+namespace fs = std::filesystem;
+using namespace xamarin::android;
+
+const std::vector<std::string> internal_pinvoke_names = {
+//	"create_public_directory",
+//	"java_interop_free",
+//	"monodroid_clear_gdb_wait",
+//	"_monodroid_counters_dump",
+//	"_monodroid_detect_cpu_and_architecture",
+//	"monodroid_dylib_mono_free",
+//	"monodroid_dylib_mono_init",
+//	"monodroid_dylib_mono_new",
+//	"monodroid_embedded_assemblies_set_assemblies_prefix",
+//	"monodroid_fopen",
+	"monodroid_free",
+//	"_monodroid_freeifaddrs",
+//	"_monodroid_gc_wait_for_bridge_processing",
+//	"_monodroid_get_dns_servers",
+//	"monodroid_get_dylib",
+//	"_monodroid_getifaddrs",
+//	"monodroid_get_namespaced_system_property",
+//	"_monodroid_get_network_interface_supports_multicast",
+//	"_monodroid_get_network_interface_up_state",
+//	"monodroid_get_system_property",
+	"_monodroid_gref_get",
+	"_monodroid_gref_log",
+	"_monodroid_gref_log_delete",
+	"_monodroid_gref_log_new",
+	"monodroid_log",
+//	"monodroid_log_traces",
+//	"_monodroid_lookup_replacement_type",
+//	"_monodroid_lookup_replacement_method_info",
+//	"_monodroid_lref_log_delete",
+//	"_monodroid_lref_log_new",
+//	"_monodroid_max_gref_get",
+//	"monodroid_strdup_printf",
+//	"monodroid_strfreev",
+//	"monodroid_strsplit",
+//	"_monodroid_timezone_get_default_id",
+//	"monodroid_timing_start",
+//	"monodroid_timing_stop",
+	"monodroid_TypeManager_get_java_class_name",
+	"clr_typemap_managed_to_java",
+	"clr_typemap_java_to_managed",
+//	"_monodroid_weak_gref_delete",
+//	"_monodroid_weak_gref_get",
+//	"_monodroid_weak_gref_new",
+//	"path_combine",
+//	"recv_uninterrupted",
+//	"send_uninterrupted",
+//	"set_world_accessable",
+
+// We can treat liblog as "internal", since we link against it
+	"__android_log_print",
+};
+
+const std::vector<std::string> dotnet_pinvoke_names = {
+	// libSystem.Globalization.Native.so
+	"GlobalizationNative_ChangeCase",
+	"GlobalizationNative_ChangeCaseInvariant",
+	"GlobalizationNative_ChangeCaseTurkish",
+	"GlobalizationNative_CloseSortHandle",
+	"GlobalizationNative_CompareString",
+	"GlobalizationNative_EndsWith",
+	"GlobalizationNative_EnumCalendarInfo",
+	"GlobalizationNative_GetCalendarInfo",
+	"GlobalizationNative_GetCalendars",
+	"GlobalizationNative_GetDefaultLocaleName",
+	"GlobalizationNative_GetICUVersion",
+	"GlobalizationNative_GetJapaneseEraStartDate",
+	"GlobalizationNative_GetLatestJapaneseEra",
+	"GlobalizationNative_GetLocaleInfoGroupingSizes",
+	"GlobalizationNative_GetLocaleInfoInt",
+	"GlobalizationNative_GetLocaleInfoString",
+	"GlobalizationNative_GetLocaleName",
+	"GlobalizationNative_GetLocales",
+	"GlobalizationNative_GetLocaleTimeFormat",
+	"GlobalizationNative_GetSortHandle",
+	"GlobalizationNative_GetSortKey",
+	"GlobalizationNative_GetSortVersion",
+	"GlobalizationNative_GetTimeZoneDisplayName",
+	"GlobalizationNative_IanaIdToWindowsId",
+	"GlobalizationNative_IndexOf",
+	"GlobalizationNative_InitICUFunctions",
+	"GlobalizationNative_InitOrdinalCasingPage",
+	"GlobalizationNative_IsNormalized",
+	"GlobalizationNative_IsPredefinedLocale",
+	"GlobalizationNative_LastIndexOf",
+	"GlobalizationNative_LoadICU",
+	"GlobalizationNative_NormalizeString",
+	"GlobalizationNative_StartsWith",
+	"GlobalizationNative_ToAscii",
+	"GlobalizationNative_ToUnicode",
+	"GlobalizationNative_WindowsIdToIanaId",
+
+	// libSystem.IO.Compression.Native.so
+	"BrotliDecoderCreateInstance",
+	"BrotliDecoderDecompress",
+	"BrotliDecoderDecompressStream",
+	"BrotliDecoderDestroyInstance",
+	"BrotliDecoderErrorString",
+	"BrotliDecoderGetErrorCode",
+	"BrotliDecoderHasMoreOutput",
+	"BrotliDecoderIsFinished",
+	"BrotliDecoderIsUsed",
+	"BrotliDecoderSetParameter",
+	"BrotliDecoderTakeOutput",
+	"BrotliDecoderVersion",
+	"BrotliDefaultAllocFunc",
+	"BrotliDefaultFreeFunc",
+	"BrotliEncoderCompress",
+	"BrotliEncoderCompressStream",
+	"BrotliEncoderCreateInstance",
+	"BrotliEncoderDestroyInstance",
+	"BrotliEncoderHasMoreOutput",
+	"BrotliEncoderIsFinished",
+	"BrotliEncoderMaxCompressedSize",
+	"BrotliEncoderSetParameter",
+	"BrotliEncoderTakeOutput",
+	"BrotliEncoderVersion",
+	"BrotliGetDictionary",
+	"BrotliGetTransforms",
+	"BrotliSetDictionaryData",
+	"BrotliTransformDictionaryWord",
+	"CompressionNative_Crc32",
+	"CompressionNative_Deflate",
+	"CompressionNative_DeflateEnd",
+	"CompressionNative_DeflateInit2_",
+	"CompressionNative_Inflate",
+	"CompressionNative_InflateEnd",
+	"CompressionNative_InflateInit2_",
+
+	// libSystem.Native.so
+	"SystemNative_Abort",
+	"SystemNative_Accept",
+	"SystemNative_Access",
+	"SystemNative_AlignedAlloc",
+	"SystemNative_AlignedFree",
+	"SystemNative_AlignedRealloc",
+	"SystemNative_Bind",
+	"SystemNative_Calloc",
+	"SystemNative_CanGetHiddenFlag",
+	"SystemNative_ChDir",
+	"SystemNative_ChMod",
+	"SystemNative_Close",
+	"SystemNative_CloseDir",
+	"SystemNative_CloseSocketEventPort",
+	"SystemNative_ConfigureTerminalForChildProcess",
+	"SystemNative_Connect",
+	"SystemNative_Connectx",
+	"SystemNative_ConvertErrorPalToPlatform",
+	"SystemNative_ConvertErrorPlatformToPal",
+	"SystemNative_CopyFile",
+	"SystemNative_CreateAutoreleasePool",
+	"SystemNative_CreateNetworkChangeListenerSocket",
+	"SystemNative_CreateSocketEventBuffer",
+	"SystemNative_CreateSocketEventPort",
+	"SystemNative_CreateThread",
+	"SystemNative_DisablePosixSignalHandling",
+	"SystemNative_Disconnect",
+	"SystemNative_DrainAutoreleasePool",
+	"SystemNative_Dup",
+	"SystemNative_EnablePosixSignalHandling",
+	"SystemNative_EnumerateGatewayAddressesForInterface",
+	"SystemNative_EnumerateInterfaceAddresses",
+	"SystemNative_Exit",
+	"SystemNative_FAllocate",
+	"SystemNative_FChflags",
+	"SystemNative_FChMod",
+	"SystemNative_FcntlCanGetSetPipeSz",
+	"SystemNative_FcntlGetFD",
+	"SystemNative_FcntlGetIsNonBlocking",
+	"SystemNative_FcntlGetPipeSz",
+	"SystemNative_FcntlSetFD",
+	"SystemNative_FcntlSetIsNonBlocking",
+	"SystemNative_FcntlSetPipeSz",
+	"SystemNative_FLock",
+	"SystemNative_ForkAndExecProcess",
+	"SystemNative_Free",
+	"SystemNative_FreeEnviron",
+	"SystemNative_FreeHostEntry",
+	"SystemNative_FreeLibrary",
+	"SystemNative_FreeSocketEventBuffer",
+	"SystemNative_FStat",
+	"SystemNative_FSync",
+	"SystemNative_FTruncate",
+	"SystemNative_FUTimens",
+	"SystemNative_GetActiveTcpConnectionInfos",
+	"SystemNative_GetActiveUdpListeners",
+	"SystemNative_GetAddressFamily",
+	"SystemNative_GetAllMountPoints",
+	"SystemNative_GetAtOutOfBandMark",
+	"SystemNative_GetBootTimeTicks",
+	"SystemNative_GetBytesAvailable",
+	"SystemNative_GetControlCharacters",
+	"SystemNative_GetControlMessageBufferSize",
+	"SystemNative_GetCpuUtilization",
+	"SystemNative_GetCryptographicallySecureRandomBytes",
+	"SystemNative_GetCwd",
+	"SystemNative_GetDefaultSearchOrderPseudoHandle",
+	"SystemNative_GetDefaultTimeZone",
+	"SystemNative_GetDeviceIdentifiers",
+	"SystemNative_GetDomainName",
+	"SystemNative_GetDomainSocketSizes",
+	"SystemNative_GetEGid",
+	"SystemNative_GetEnv",
+	"SystemNative_GetEnviron",
+	"SystemNative_GetErrNo",
+	"SystemNative_GetEstimatedTcpConnectionCount",
+	"SystemNative_GetEstimatedUdpListenerCount",
+	"SystemNative_GetEUid",
+	"SystemNative_GetFileSystemType",
+	"SystemNative_GetFormatInfoForMountPoint",
+	"SystemNative_GetGroupList",
+	"SystemNative_GetGroupName",
+	"SystemNative_GetGroups",
+	"SystemNative_GetHostEntryForName",
+	"SystemNative_GetHostName",
+	"SystemNative_GetIcmpv4GlobalStatistics",
+	"SystemNative_GetIcmpv6GlobalStatistics",
+	"SystemNative_GetIPv4Address",
+	"SystemNative_GetIPv4GlobalStatistics",
+	"SystemNative_GetIPv4MulticastOption",
+	"SystemNative_GetIPv6Address",
+	"SystemNative_GetIPv6MulticastOption",
+	"SystemNative_GetLingerOption",
+	"SystemNative_GetLoadLibraryError",
+	"SystemNative_GetMaximumAddressSize",
+	"SystemNative_GetNameInfo",
+	"SystemNative_GetNativeIPInterfaceStatistics",
+	"SystemNative_GetNetworkInterfaces",
+	"SystemNative_GetNonCryptographicallySecureRandomBytes",
+	"SystemNative_GetNumRoutes",
+	"SystemNative_GetOSArchitecture",
+	"SystemNative_GetPeerID",
+	"SystemNative_GetPeerName",
+	"SystemNative_GetPid",
+	"SystemNative_GetPlatformSignalNumber",
+	"SystemNative_GetPort",
+	"SystemNative_GetPriority",
+	"SystemNative_GetProcAddress",
+	"SystemNative_GetProcessPath",
+	"SystemNative_GetPwNamR",
+	"SystemNative_GetPwUidR",
+	"SystemNative_GetRawSockOpt",
+	"SystemNative_GetReadDirRBufferSize",
+	"SystemNative_GetRLimit",
+	"SystemNative_GetSid",
+	"SystemNative_GetSignalForBreak",
+	"SystemNative_GetSocketAddressSizes",
+	"SystemNative_GetSocketErrorOption",
+	"SystemNative_GetSocketType",
+	"SystemNative_GetSockName",
+	"SystemNative_GetSockOpt",
+	"SystemNative_GetSpaceInfoForMountPoint",
+	"SystemNative_GetSystemTimeAsTicks",
+	"SystemNative_GetTcpGlobalStatistics",
+	"SystemNative_GetTimestamp",
+	"SystemNative_GetTimeZoneData",
+	"SystemNative_GetUdpGlobalStatistics",
+	"SystemNative_GetUInt64OSThreadId",
+	"SystemNative_GetUnixRelease",
+	"SystemNative_GetUnixVersion",
+	"SystemNative_GetWindowSize",
+	"SystemNative_HandleNonCanceledPosixSignal",
+	"SystemNative_InitializeConsoleBeforeRead",
+	"SystemNative_InitializeTerminalAndSignalHandling",
+	"SystemNative_INotifyAddWatch",
+	"SystemNative_INotifyInit",
+	"SystemNative_INotifyRemoveWatch",
+	"SystemNative_InterfaceNameToIndex",
+	"SystemNative_iOSSupportVersion",
+	"SystemNative_IsATty",
+	"SystemNative_Kill",
+	"SystemNative_LChflags",
+	"SystemNative_LChflagsCanSetHiddenFlag",
+	"SystemNative_Link",
+	"SystemNative_Listen",
+	"SystemNative_LoadLibrary",
+	"SystemNative_LockFileRegion",
+	"SystemNative_Log",
+	"SystemNative_LogError",
+	"SystemNative_LowLevelMonitor_Acquire",
+	"SystemNative_LowLevelMonitor_Create",
+	"SystemNative_LowLevelMonitor_Destroy",
+	"SystemNative_LowLevelMonitor_Release",
+	"SystemNative_LowLevelMonitor_Signal_Release",
+	"SystemNative_LowLevelMonitor_TimedWait",
+	"SystemNative_LowLevelMonitor_Wait",
+	"SystemNative_LSeek",
+	"SystemNative_LStat",
+	"SystemNative_MAdvise",
+	"SystemNative_Malloc",
+	"SystemNative_MapTcpState",
+	"SystemNative_MkDir",
+	"SystemNative_MkdTemp",
+	"SystemNative_MkFifo",
+	"SystemNative_MkNod",
+	"SystemNative_MksTemps",
+	"SystemNative_MMap",
+	"SystemNative_MProtect",
+	"SystemNative_MSync",
+	"SystemNative_MUnmap",
+	"SystemNative_Open",
+	"SystemNative_OpenDir",
+	"SystemNative_PathConf",
+	"SystemNative_Pipe",
+	"SystemNative_PlatformSupportsDualModeIPv4PacketInfo",
+	"SystemNative_Poll",
+	"SystemNative_PosixFAdvise",
+	"SystemNative_PRead",
+	"SystemNative_PReadV",
+	"SystemNative_PWrite",
+	"SystemNative_PWriteV",
+	"SystemNative_Read",
+	"SystemNative_ReadDirR",
+	"SystemNative_ReadEvents",
+	"SystemNative_ReadLink",
+	"SystemNative_ReadProcessStatusInfo",
+	"SystemNative_ReadStdin",
+	"SystemNative_Realloc",
+	"SystemNative_RealPath",
+	"SystemNative_Receive",
+	"SystemNative_ReceiveMessage",
+	"SystemNative_ReceiveSocketError",
+	"SystemNative_RegisterForSigChld",
+	"SystemNative_Rename",
+	"SystemNative_RmDir",
+	"SystemNative_SchedGetAffinity",
+	"SystemNative_SchedGetCpu",
+	"SystemNative_SchedSetAffinity",
+	"SystemNative_SearchPath",
+	"SystemNative_SearchPath_TempDirectory",
+	"SystemNative_Send",
+	"SystemNative_SendFile",
+	"SystemNative_SendMessage",
+	"SystemNative_SetAddressFamily",
+	"SystemNative_SetDelayedSigChildConsoleConfigurationHandler",
+	"SystemNative_SetErrNo",
+	"SystemNative_SetEUid",
+	"SystemNative_SetIPv4Address",
+	"SystemNative_SetIPv4MulticastOption",
+	"SystemNative_SetIPv6Address",
+	"SystemNative_SetIPv6MulticastOption",
+	"SystemNative_SetKeypadXmit",
+	"SystemNative_SetLingerOption",
+	"SystemNative_SetPort",
+	"SystemNative_SetPosixSignalHandler",
+	"SystemNative_SetPriority",
+	"SystemNative_SetRawSockOpt",
+	"SystemNative_SetReceiveTimeout",
+	"SystemNative_SetRLimit",
+	"SystemNative_SetSendTimeout",
+	"SystemNative_SetSignalForBreak",
+	"SystemNative_SetSockOpt",
+	"SystemNative_SetTerminalInvalidationHandler",
+	"SystemNative_SetWindowSize",
+	"SystemNative_ShmOpen",
+	"SystemNative_ShmUnlink",
+	"SystemNative_Shutdown",
+	"SystemNative_SNPrintF",
+	"SystemNative_SNPrintF_1I",
+	"SystemNative_SNPrintF_1S",
+	"SystemNative_Socket",
+	"SystemNative_Stat",
+	"SystemNative_StdinReady",
+	"SystemNative_StrErrorR",
+	"SystemNative_SymLink",
+	"SystemNative_Sync",
+	"SystemNative_SysConf",
+	"SystemNative_Sysctl",
+	"SystemNative_SysLog",
+	"SystemNative_TryChangeSocketEventRegistration",
+	"SystemNative_TryGetIPPacketInformation",
+	"SystemNative_TryGetUInt32OSThreadId",
+	"SystemNative_UninitializeConsoleAfterRead",
+	"SystemNative_Unlink",
+	"SystemNative_UTimensat",
+	"SystemNative_WaitForSocketEvents",
+	"SystemNative_WaitIdAnyExitedNoHangNoWait",
+	"SystemNative_WaitPidExitedNoHang",
+	"SystemNative_Write",
+
+	// libSystem.Security.Cryptography.Native.Android.so
+	"AndroidCryptoNative_AeadCipherFinalEx",
+	"AndroidCryptoNative_Aes128Cbc",
+	"AndroidCryptoNative_Aes128Ccm",
+	"AndroidCryptoNative_Aes128Cfb128",
+	"AndroidCryptoNative_Aes128Cfb8",
+	"AndroidCryptoNative_Aes128Ecb",
+	"AndroidCryptoNative_Aes128Gcm",
+	"AndroidCryptoNative_Aes192Cbc",
+	"AndroidCryptoNative_Aes192Ccm",
+	"AndroidCryptoNative_Aes192Cfb128",
+	"AndroidCryptoNative_Aes192Cfb8",
+	"AndroidCryptoNative_Aes192Ecb",
+	"AndroidCryptoNative_Aes192Gcm",
+	"AndroidCryptoNative_Aes256Cbc",
+	"AndroidCryptoNative_Aes256Ccm",
+	"AndroidCryptoNative_Aes256Cfb128",
+	"AndroidCryptoNative_Aes256Cfb8",
+	"AndroidCryptoNative_Aes256Ecb",
+	"AndroidCryptoNative_Aes256Gcm",
+	"AndroidCryptoNative_BigNumToBinary",
+	"AndroidCryptoNative_ChaCha20Poly1305",
+	"AndroidCryptoNative_CipherCreate",
+	"AndroidCryptoNative_CipherCreatePartial",
+	"AndroidCryptoNative_CipherCtxSetPadding",
+	"AndroidCryptoNative_CipherDestroy",
+	"AndroidCryptoNative_CipherFinalEx",
+	"AndroidCryptoNative_CipherIsSupported",
+	"AndroidCryptoNative_CipherReset",
+	"AndroidCryptoNative_CipherSetKeyAndIV",
+	"AndroidCryptoNative_CipherSetNonceLength",
+	"AndroidCryptoNative_CipherSetTagLength",
+	"AndroidCryptoNative_CipherUpdate",
+	"AndroidCryptoNative_CipherUpdateAAD",
+	"AndroidCryptoNative_DecodeRsaSubjectPublicKeyInfo",
+	"AndroidCryptoNative_DeleteGlobalReference",
+	"AndroidCryptoNative_Des3Cbc",
+	"AndroidCryptoNative_Des3Cfb64",
+	"AndroidCryptoNative_Des3Cfb8",
+	"AndroidCryptoNative_Des3Ecb",
+	"AndroidCryptoNative_DesCbc",
+	"AndroidCryptoNative_DesCfb8",
+	"AndroidCryptoNative_DesEcb",
+	"AndroidCryptoNative_DsaGenerateKey",
+	"AndroidCryptoNative_DsaKeyCreateByExplicitParameters",
+	"AndroidCryptoNative_DsaSign",
+	"AndroidCryptoNative_DsaSignatureFieldSize",
+	"AndroidCryptoNative_DsaSizeP",
+	"AndroidCryptoNative_DsaSizeSignature",
+	"AndroidCryptoNative_DsaVerify",
+	"AndroidCryptoNative_EcdhDeriveKey",
+	"AndroidCryptoNative_EcDsaSign",
+	"AndroidCryptoNative_EcDsaSize",
+	"AndroidCryptoNative_EcDsaVerify",
+	"AndroidCryptoNative_EcKeyCreateByExplicitParameters",
+	"AndroidCryptoNative_EcKeyCreateByKeyParameters",
+	"AndroidCryptoNative_EcKeyCreateByOid",
+	"AndroidCryptoNative_EcKeyDestroy",
+	"AndroidCryptoNative_EcKeyGetCurveName",
+	"AndroidCryptoNative_EcKeyGetSize",
+	"AndroidCryptoNative_EcKeyUpRef",
+	"AndroidCryptoNative_GetBigNumBytes",
+	"AndroidCryptoNative_GetDsaParameters",
+	"AndroidCryptoNative_GetECCurveParameters",
+	"AndroidCryptoNative_GetECKeyParameters",
+	"AndroidCryptoNative_GetRsaParameters",
+	"AndroidCryptoNative_NewGlobalReference",
+	"AndroidCryptoNative_Pbkdf2",
+	"AndroidCryptoNative_RegisterRemoteCertificateValidationCallback",
+	"AndroidCryptoNative_RsaCreate",
+	"AndroidCryptoNative_RsaDestroy",
+	"AndroidCryptoNative_RsaGenerateKeyEx",
+	"AndroidCryptoNative_RsaPrivateDecrypt",
+	"AndroidCryptoNative_RsaPublicEncrypt",
+	"AndroidCryptoNative_RsaSignPrimitive",
+	"AndroidCryptoNative_RsaSize",
+	"AndroidCryptoNative_RsaUpRef",
+	"AndroidCryptoNative_RsaVerificationPrimitive",
+	"AndroidCryptoNative_SetRsaParameters",
+	"AndroidCryptoNative_SSLGetSupportedProtocols",
+	"AndroidCryptoNative_SSLStreamCreate",
+	"AndroidCryptoNative_SSLStreamCreateWithCertificates",
+	"AndroidCryptoNative_SSLStreamCreateWithKeyStorePrivateKeyEntry",
+	"AndroidCryptoNative_SSLStreamGetApplicationProtocol",
+	"AndroidCryptoNative_SSLStreamGetCipherSuite",
+	"AndroidCryptoNative_SSLStreamGetPeerCertificate",
+	"AndroidCryptoNative_SSLStreamGetPeerCertificates",
+	"AndroidCryptoNative_SSLStreamGetProtocol",
+	"AndroidCryptoNative_SSLStreamHandshake",
+	"AndroidCryptoNative_SSLStreamInitialize",
+	"AndroidCryptoNative_SSLStreamIsLocalCertificateUsed",
+	"AndroidCryptoNative_SSLStreamRead",
+	"AndroidCryptoNative_SSLStreamRelease",
+	"AndroidCryptoNative_SSLStreamRequestClientAuthentication",
+	"AndroidCryptoNative_SSLStreamSetApplicationProtocols",
+	"AndroidCryptoNative_SSLStreamSetEnabledProtocols",
+	"AndroidCryptoNative_SSLStreamSetTargetHost",
+	"AndroidCryptoNative_SSLStreamShutdown",
+	"AndroidCryptoNative_SSLStreamVerifyHostname",
+	"AndroidCryptoNative_SSLStreamWrite",
+	"AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration",
+	"AndroidCryptoNative_X509ChainBuild",
+	"AndroidCryptoNative_X509ChainCreateContext",
+	"AndroidCryptoNative_X509ChainDestroyContext",
+	"AndroidCryptoNative_X509ChainGetCertificateCount",
+	"AndroidCryptoNative_X509ChainGetCertificates",
+	"AndroidCryptoNative_X509ChainGetErrorCount",
+	"AndroidCryptoNative_X509ChainGetErrors",
+	"AndroidCryptoNative_X509ChainSetCustomTrustStore",
+	"AndroidCryptoNative_X509ChainValidate",
+	"AndroidCryptoNative_X509Decode",
+	"AndroidCryptoNative_X509DecodeCollection",
+	"AndroidCryptoNative_X509Encode",
+	"AndroidCryptoNative_X509ExportPkcs7",
+	"AndroidCryptoNative_X509GetCertificateForPrivateKeyEntry",
+	"AndroidCryptoNative_X509GetContentType",
+	"AndroidCryptoNative_X509IsKeyStorePrivateKeyEntry",
+	"AndroidCryptoNative_X509PublicKey",
+	"AndroidCryptoNative_X509StoreAddCertificate",
+	"AndroidCryptoNative_X509StoreAddCertificateWithPrivateKey",
+	"AndroidCryptoNative_X509StoreContainsCertificate",
+	"AndroidCryptoNative_X509StoreDeleteEntry",
+	"AndroidCryptoNative_X509StoreEnumerateCertificates",
+	"AndroidCryptoNative_X509StoreEnumerateTrustedCertificates",
+	"AndroidCryptoNative_X509StoreGetPrivateKeyEntry",
+	"AndroidCryptoNative_X509StoreOpenDefault",
+	"AndroidCryptoNative_X509StoreRemoveCertificate",
+	"CryptoNative_EnsureOpenSslInitialized",
+	"CryptoNative_ErrClearError",
+	"CryptoNative_ErrErrorStringN",
+	"CryptoNative_ErrGetErrorAlloc",
+	"CryptoNative_ErrPeekError",
+	"CryptoNative_ErrPeekLastError",
+	"CryptoNative_ErrReasonErrorString",
+	"CryptoNative_EvpDigestCurrent",
+	"CryptoNative_EvpDigestFinalEx",
+	"CryptoNative_EvpDigestOneShot",
+	"CryptoNative_EvpDigestReset",
+	"CryptoNative_EvpDigestUpdate",
+	"CryptoNative_EvpMd5",
+	"CryptoNative_EvpMdCtxCopyEx",
+	"CryptoNative_EvpMdCtxCreate",
+	"CryptoNative_EvpMdCtxDestroy",
+	"CryptoNative_EvpMdSize",
+	"CryptoNative_EvpSha1",
+	"CryptoNative_EvpSha256",
+	"CryptoNative_EvpSha384",
+	"CryptoNative_EvpSha512",
+	"CryptoNative_GetMaxMdSize",
+	"CryptoNative_GetRandomBytes",
+	"CryptoNative_HmacCreate",
+	"CryptoNative_HmacCurrent",
+	"CryptoNative_HmacDestroy",
+	"CryptoNative_HmacFinal",
+	"CryptoNative_HmacOneShot",
+	"CryptoNative_HmacReset",
+	"CryptoNative_HmacUpdate",
+	"Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate",
+};
+
+template<typename Hash>
+struct PinvokeEntry
+{
+	std::string name;
+	Hash hash;
+	bool write_func_pointer;
+
+	template<class Os> friend
+	Os& operator<< (Os& os, PinvokeEntry<Hash> const& p)
+	{
+		os << std::showbase << std::hex << p.hash << ", \"" << p.name << "\", ";
+
+		if (p.write_func_pointer) {
+			return os << "reinterpret_cast<void*>(&" << p.name << ")";
+		}
+
+		return os << "nullptr";
+	}
+};
+
+void print (std::ostream& os, std::string comment, std::string variable_name, auto const& seq)
+{
+	os << "\t//" << comment << '\n';
+	os << "\tstd::array<PinvokeEntry, " << std::dec << seq.size () << "> " << variable_name << " {{" << std::endl;
+
+	for (auto const& elem : seq) {
+		os << "\t\t{" << elem << "}," << std::endl;
+	}
+
+	os << "\t}};" << std::endl << std::endl;
+}
+
+template<typename Hash>
+bool add_hash (std::string const& pinvoke, Hash hash, std::vector<PinvokeEntry<Hash>>& vec, std::unordered_set<Hash>& used_cache, bool write_func_pointer)
+{
+	vec.emplace_back (pinvoke, hash, write_func_pointer);
+	if (used_cache.contains (hash)) {
+		std::cerr << (sizeof(Hash) == 4 ? "32" : "64") << "-bit hash collision for key '" << pinvoke << "': " << std::hex << std::showbase << hash << std::endl;
+		return true;
+	}
+
+	used_cache.insert (hash);
+	return false;
+}
+
+bool generate_hashes (std::string table_name, std::vector<std::string> const& names, std::vector<PinvokeEntry<uint32_t>>& pinvokes32, std::vector<PinvokeEntry<uint64_t>>& pinvokes64, bool write_func_pointer)
+{
+	std::unordered_set<uint32_t> used_pinvokes32{};
+	std::unordered_set<uint64_t> used_pinvokes64{};
+	uint32_t hash32;
+	uint64_t hash64;
+	bool have_collisions = false;
+
+	std::cout << "There are " << names.size () << " " << table_name << " p/invoke functions" << std::endl;
+	for (std::string const& pinvoke : names) {
+		have_collisions |= add_hash (pinvoke, xxhash32::hash (pinvoke.c_str (), pinvoke.length ()), pinvokes32, used_pinvokes32, write_func_pointer);
+		have_collisions |= add_hash (pinvoke, xxhash64::hash (pinvoke.c_str (), pinvoke.length ()), pinvokes64, used_pinvokes64, write_func_pointer);
+	}
+
+	std::cout << "p/invoke hash collisions for '" << table_name << "' were " << (have_collisions ? "" : "not ") << "found" << std::endl;
+
+	std::ranges::sort (pinvokes32, {}, &PinvokeEntry<uint32_t>::hash);
+	std::ranges::sort (pinvokes64, {}, &PinvokeEntry<uint64_t>::hash);
+
+	return have_collisions;
+}
+
+template<typename Hash>
+void write_library_name_hash (Hash (*hasher)(const char*, size_t), std::ostream& os, std::string library_name, std::string variable_prefix)
+{
+	Hash hash = hasher (library_name.c_str (), library_name.length ());
+	os << "constexpr hash_t " << variable_prefix << "_library_hash = " << std::hex << hash << ";" << std::endl;
+}
+
+template<typename Hash>
+void write_library_name_hashes (Hash (*hasher)(const char*, size_t), std::ostream& output)
+{
+	write_library_name_hash (hasher, output, "java-interop", "java_interop");
+	write_library_name_hash (hasher, output, "xa-internal-api", "xa_internal_api");
+	write_library_name_hash (hasher, output, "liblog", "android_liblog");
+	write_library_name_hash (hasher, output, "libSystem.Native", "system_native");
+	write_library_name_hash (hasher, output, "libSystem.IO.Compression.Native", "system_io_compression_native");
+	write_library_name_hash (hasher, output, "libSystem.Security.Cryptography.Native.Android", "system_security_cryptography_native_android");
+	write_library_name_hash (hasher, output, "libSystem.Globalization.Native", "system_globalization_native");
+}
+
+int main (int argc, char **argv)
+{
+	if (argc < 2) {
+		std::cerr << "Usage: generate-pinvoke-tables OUTPUT_FILE_PATH" << std::endl << std::endl;
+		return 1;
+	}
+
+	fs::path output_file_path {argv[1]};
+
+	if (fs::exists (output_file_path)) {
+		if (fs::is_directory (output_file_path)) {
+			std::cerr << "Output destination '" << output_file_path << "' is a directory" << std::endl;
+			return 1;
+		}
+
+		fs::remove (output_file_path);
+	} else {
+		fs::path file_dir = output_file_path.parent_path ();
+		if (fs::exists (file_dir)) {
+			if (!fs::is_directory (file_dir)) {
+				std::cerr << "Output destination parent path points to a file ('" << file_dir << "'" << std::endl;
+				return 1;
+			}
+		} else if (!file_dir.empty ()) {
+			if (!fs::create_directories (file_dir)) {
+				std::cerr << "Failed to create output directory '" << file_dir << "'" << std::endl;
+				std::cerr << strerror (errno) << std::endl;
+				return 1;
+			}
+		}
+	}
+
+	bool have_collisions = false;
+	std::vector<PinvokeEntry<uint32_t>> internal_pinvokes32{};
+	std::vector<PinvokeEntry<uint64_t>> internal_pinvokes64{};
+	have_collisions |= generate_hashes ("internal", internal_pinvoke_names, internal_pinvokes32, internal_pinvokes64, true);
+
+	std::vector<PinvokeEntry<uint32_t>> dotnet_pinvokes32{};
+	std::vector<PinvokeEntry<uint64_t>> dotnet_pinvokes64{};
+	have_collisions |= generate_hashes ("dotnet", dotnet_pinvoke_names, dotnet_pinvokes32, dotnet_pinvokes64, false);
+
+	std::cout << "Generating tables in file: " << output_file_path << std::endl;
+
+	std::ofstream output {output_file_path, std::ios::binary};
+
+	output << "//" << std::endl;
+	output << "// Autogenarated file. DO NOT EDIT." << std::endl;
+	output << "//" << std::endl;
+	output << "// To regenerate run ../../../../build-tools/scripts/generate-pinvoke-tables.sh on Linux or macOS" << std::endl;
+	output << "// A compiler with support for C++20 ranges is required" << std::endl;
+	output << "//" << std::endl << std::endl;
+
+	output << "#include <array>" << std::endl;
+	output << "#include <cstdint>" << std::endl << std::endl;
+
+	output << "namespace {" << std::endl;
+	output << "#if INTPTR_MAX == INT64_MAX" << std::endl;
+	print (output, "64-bit internal p/invoke table", "internal_pinvokes", internal_pinvokes64);
+	print (output, "64-bit DotNet p/invoke table", "dotnet_pinvokes", dotnet_pinvokes64);
+	output << std::endl;
+	write_library_name_hashes<uint64_t> (xxhash64::hash, output);
+
+	output << "#else" << std::endl;
+
+	print (output, "32-bit internal p/invoke table", "internal_pinvokes", internal_pinvokes32);
+	print (output, "32-bit DotNet p/invoke table", "dotnet_pinvokes", dotnet_pinvokes32);
+	output << std::endl;
+	write_library_name_hashes<uint32_t> (xxhash32::hash, output);
+
+	output << "#endif" << std::endl << std::endl;
+
+	output << "constexpr size_t internal_pinvokes_count = " << std::dec << std::noshowbase << internal_pinvoke_names.size () << ";" << std::endl;
+	output << "constexpr size_t dotnet_pinvokes_count = " << std::dec << std::noshowbase << dotnet_pinvoke_names.size () << ";" << std::endl;
+	output << "} // end of anonymous namespace" << std::endl;
+
+	return have_collisions ? 1 : 0;
+}
+
+// This serves as a quick compile-time test of the algorithm's correctness.
+// The tests are copied from https://github.com/ekpyron/xxhashct/test.cpp
+
+template<uint64_t value, uint64_t expected>
+struct constexpr_test {
+	static_assert (value == expected, "Compile-time hash mismatch.");
+};
+
+constexpr_test<xxhash32::hash<0> ("", 0), 0x2CC5D05U> constexprTest_1;
+constexpr_test<xxhash32::hash<2654435761U> ("", 0), 0x36B78AE7U> constexprTest_2;
+//constexpr_test<xxhash64::hash<0> ("", 0), 0xEF46DB3751D8E999ULL> constexprTest_3;
+//constexpr_test<xxhash64::hash<2654435761U> ("", 0), 0xAC75FDA2929B17EFULL> constexprTest_4;
+constexpr_test<xxhash32::hash<0> ("test", 4), 0x3E2023CFU> constexprTest32_5;
+constexpr_test<xxhash32::hash<2654435761U> ("test", 4), 0xA9C14438U> constexprTest32_6;
+//constexpr_test<xxhash64::hash<0> ("test", 4), 0x4fdcca5ddb678139ULL> constexprTest64_7;
+//constexpr_test<xxhash64::hash<2654435761U> ("test", 4), 0x5A183B8150E2F651ULL> constexprTest64_8;
diff --git a/src/native/clr/host/host-jni.cc b/src/native/clr/host/host-jni.cc
new file mode 100644
index 00000000000..1b72015fc24
--- /dev/null
+++ b/src/native/clr/host/host-jni.cc
@@ -0,0 +1,60 @@
+#include <host/host.hh>
+#include <host/host-jni.hh>
+#include <shared/log_types.hh>
+
+using namespace xamarin::android;
+
+JNIEXPORT jint JNICALL
+JNI_OnLoad (JavaVM *vm, void *reserved)
+{
+	log_write (LOG_DEFAULT, LogLevel::Info, "JNI_OnLoad");
+
+	return Host::Java_JNI_OnLoad (vm, reserved);
+}
+
+JNIEXPORT void
+JNICALL Java_mono_android_Runtime_dumpTimingData ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jclass klass)
+{
+	// if (internal_timing == nullptr) {
+	// 	return;
+	// }
+
+	// internal_timing.dump ();
+}
+
+JNIEXPORT void
+JNICALL Java_mono_android_Runtime_register (JNIEnv *env, [[maybe_unused]] jclass klass, jstring managedType, jclass nativeClass, jstring methods)
+{
+	Host::Java_mono_android_Runtime_register (env, managedType, nativeClass, methods);
+}
+
+JNIEXPORT void JNICALL
+Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava,
+	jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader,
+	jobjectArray assembliesJava, jboolean isEmulator,
+	jboolean haveSplitApks)
+{
+	Host::Java_mono_android_Runtime_initInternal (
+		env,
+		klass,
+		lang,
+		runtimeApksJava,
+		runtimeNativeLibDir,
+		appDirs,
+		localDateTimeOffset,
+		loader,
+		assembliesJava,
+		isEmulator,
+		haveSplitApks
+	);
+}
+
+JNIEXPORT void
+JNICALL Java_mono_android_Runtime_propagateUncaughtException (JNIEnv *env, [[maybe_unused]] jclass klass, jobject javaThread, jthrowable javaException)
+{
+}
+
+JNIEXPORT void
+JNICALL Java_mono_android_Runtime_notifyTimeZoneChanged ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jclass klass)
+{
+}
diff --git a/src/native/clr/host/host-util.cc b/src/native/clr/host/host-util.cc
new file mode 100644
index 00000000000..1a1e49d8e34
--- /dev/null
+++ b/src/native/clr/host/host-util.cc
@@ -0,0 +1,19 @@
+#include <host/host-util.hh>
+#include <host/os-bridge.hh>
+
+using namespace xamarin::android;
+
+auto HostUtil::get_class_from_runtime_field (JNIEnv *env, jclass runtime, const char *name, bool make_gref) noexcept -> jclass
+{
+	static constexpr char java_lang_class_sig[] = "Ljava/lang/Class;";
+
+	jfieldID fieldID = env->GetStaticFieldID (runtime, name, java_lang_class_sig);
+	if (fieldID == nullptr)
+		return nullptr;
+
+	jobject field = env->GetStaticObjectField (runtime, fieldID);
+	if (field == nullptr)
+		return nullptr;
+
+	return reinterpret_cast<jclass> (make_gref ? OSBridge::lref_to_gref (env, field) : field);
+}
diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc
new file mode 100644
index 00000000000..a2eedaa2d6d
--- /dev/null
+++ b/src/native/clr/host/host.cc
@@ -0,0 +1,393 @@
+#include <cstdio>
+
+#include <coreclrhost.h>
+
+#include <xamarin-app.hh>
+#include <host/assembly-store.hh>
+#include <host/host.hh>
+#include <host/host-jni.hh>
+#include <host/host-util.hh>
+#include <host/os-bridge.hh>
+#include <runtime-base/android-system.hh>
+#include <runtime-base/jni-wrappers.hh>
+#include <runtime-base/logger.hh>
+#include <runtime-base/timing-internal.hh>
+#include <shared/log_types.hh>
+#include <startup/zip.hh>
+
+using namespace xamarin::android;
+
+void Host::clr_error_writer (const char *message) noexcept
+{
+	log_error (LOG_DEFAULT, "CLR error: {}", optional_string (message));
+}
+
+size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, void *contract_context) noexcept
+{
+	log_debug (LOG_DEFAULT, "clr_get_runtime_property (\"{}\"...)", key);
+	return 0;
+}
+
+bool Host::clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept
+{
+	// TODO: `path` might be a full path, make sure it isn't
+	log_debug (LOG_DEFAULT, "clr_external_assembly_probe (\"{}\"...)", path);
+	if (data_start == nullptr || size == nullptr) {
+		return false; // TODO: abort instead?
+	}
+
+	*data_start = AssemblyStore::open_assembly (path, *size);
+	log_debug (
+		LOG_ASSEMBLY,
+		"Assembly data {}mapped ({:p}, {} bytes)",
+		*data_start == nullptr ? "not "sv : ""sv,
+		*data_start,
+		*size
+	);
+
+	return *data_start != nullptr && *size > 0;
+}
+
+auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dynamic_local_string<SENSIBLE_PATH_MAX> const& entry_name, uint32_t offset, uint32_t size) -> bool
+{
+	log_debug (LOG_ASSEMBLY, "zip entry: {}", entry_name.get ());
+	if (!found_assembly_store) {
+		found_assembly_store = Zip::assembly_store_file_path.compare (0, entry_name.length (), entry_name.get ()) == 0;
+		if (found_assembly_store) {
+			log_debug (LOG_ASSEMBLY, "Found assembly store in '{}': {}", apk_path, Zip::assembly_store_file_path);
+			AssemblyStore::map (apk_fd, apk_path, Zip::assembly_store_file_path, offset, size);
+			return false; // This will make the scanner keep the APK open
+		}
+	}
+	return false;
+}
+
+void Host::gather_assemblies_and_libraries (jstring_array_wrapper& runtimeApks, bool have_split_apks)
+{
+	if (!AndroidSystem::is_embedded_dso_mode_enabled ()) {
+		Helpers::abort_application ("Filesystem mode not supported yet.");
+	}
+
+	int64_t apk_count = static_cast<int64_t>(runtimeApks.get_length ());
+	bool got_split_config_abi_apk = false;
+
+	for (int64_t i = 0; i < apk_count; i++) {
+		std::string_view apk_file = runtimeApks [static_cast<size_t>(i)].get_string_view ();
+
+		if (have_split_apks) {
+			bool scan_apk = false;
+
+			// With split configs we need to scan only the abi apk, because both the assembly stores and the runtime
+			// configuration blob are in `lib/{ARCH}`, which in turn lives in the split config APK
+			if (!got_split_config_abi_apk && apk_file.ends_with (Constants::split_config_abi_apk_name.data ())) {
+				got_split_config_abi_apk = scan_apk = true;
+			}
+
+			if (!scan_apk) {
+				continue;
+			}
+		}
+
+		Zip::scan_archive (apk_file, zip_scan_callback);
+	}
+}
+
+void Host::create_xdg_directory (jstring_wrapper& home, size_t home_len, std::string_view const& relative_path, std::string_view const& environment_variable_name) noexcept
+{
+	static_local_string<SENSIBLE_PATH_MAX> dir (home_len + relative_path.length ());
+	Util::path_combine (dir, home.get_string_view (), relative_path);
+
+	log_debug (LOG_DEFAULT, "Creating XDG directory: {}", optional_string (dir.get ()));
+	int rv = Util::create_directory (dir.get (), Constants::DEFAULT_DIRECTORY_MODE);
+	if (rv < 0 && errno != EEXIST) {
+		log_warn (LOG_DEFAULT, "Failed to create XDG directory {}. {}", optional_string (dir.get ()), strerror (errno));
+	}
+
+	if (!environment_variable_name.empty ()) {
+		setenv (environment_variable_name.data (), dir.get (), 1);
+	}
+}
+
+void Host::create_xdg_directories_and_environment (jstring_wrapper &homeDir) noexcept
+{
+	size_t home_len = strlen (homeDir.get_cstr ());
+
+	constexpr auto XDG_DATA_HOME = "XDG_DATA_HOME"sv;
+	constexpr auto HOME_PATH = ".local/share"sv;
+	create_xdg_directory (homeDir, home_len, HOME_PATH, XDG_DATA_HOME);
+
+	constexpr auto XDG_CONFIG_HOME = "XDG_CONFIG_HOME"sv;
+	constexpr auto CONFIG_PATH = ".config"sv;
+	create_xdg_directory (homeDir, home_len, CONFIG_PATH, XDG_CONFIG_HOME);
+}
+
+[[gnu::always_inline]]
+auto Host::create_delegate (
+	std::string_view const& assembly_name, std::string_view const& type_name,
+	std::string_view const& method_name) noexcept -> void*
+{
+	void *delegate = nullptr;
+	int hr = coreclr_create_delegate (
+		clr_host,
+		domain_id,
+		assembly_name.data (),
+		type_name.data (),
+		method_name.data (),
+		&delegate
+	);
+	log_debug (LOG_ASSEMBLY,
+			   "{}@{}.{} delegate creation result == {:x}; delegate == {:p}",
+			   assembly_name,
+			   type_name,
+			   method_name,
+			   static_cast<unsigned int>(hr),
+			   delegate
+	);
+
+	// TODO: make S_OK & friends known to us
+	if (hr != 0 /* S_OK */) {
+		Helpers::abort_application (
+			LOG_DEFAULT,
+			std::format (
+				"Failed to create delegate for {}.{}.{} (result == {:x})",
+				assembly_name,
+				type_name,
+				method_name,
+				hr)
+		);
+	}
+
+	return delegate;
+}
+
+void Host::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass runtimeClass, jstring lang, jobjectArray runtimeApksJava,
+	jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader,
+	jobjectArray assembliesJava, jboolean isEmulator, jboolean haveSplitApks) noexcept
+{
+	Logger::init_logging_categories ();
+
+	// If fast logging is disabled, log messages immediately
+	FastTiming::initialize ((Logger::log_timing_categories() & LogTimingCategories::FastBare) != LogTimingCategories::FastBare);
+
+	size_t total_time_index;
+	if (FastTiming::enabled ()) [[unlikely]] {
+		_timing = std::make_unique<Timing> ();
+		total_time_index = internal_timing.start_event (TimingEventKind::TotalRuntimeInit);
+	}
+
+	jstring_array_wrapper applicationDirs (env, appDirs);
+
+	jstring_wrapper jstr (env, lang);
+	Util::set_environment_variable ("LANG", jstr);
+
+	jstring_wrapper &home = applicationDirs[Constants::APP_DIRS_FILES_DIR_INDEX];
+	Util::set_environment_variable_for_directory ("TMPDIR", applicationDirs[Constants::APP_DIRS_CACHE_DIR_INDEX]);
+	Util::set_environment_variable_for_directory ("HOME", home);
+	create_xdg_directories_and_environment (home);
+
+	AndroidSystem::detect_embedded_dso_mode (applicationDirs);
+	AndroidSystem::set_running_in_emulator (isEmulator);
+	AndroidSystem::set_primary_override_dir (home);
+	AndroidSystem::create_update_dir (AndroidSystem::get_primary_override_dir ());
+	AndroidSystem::setup_environment ();
+
+	jstring_array_wrapper runtimeApks (env, runtimeApksJava);
+	AndroidSystem::setup_app_library_directories (runtimeApks, applicationDirs, haveSplitApks);
+
+	gather_assemblies_and_libraries (runtimeApks, haveSplitApks);
+
+	size_t clr_init_time_index;
+	if (FastTiming::enabled ()) [[unlikely]] {
+		clr_init_time_index = internal_timing.start_event (TimingEventKind::MonoRuntimeInit);
+	}
+
+	coreclr_set_error_writer (clr_error_writer);
+	// We REALLY shouldn't be doing this
+	snprintf (host_contract_ptr_buffer.data (), host_contract_ptr_buffer.size (), "%p", &runtime_contract);
+
+	// The first entry in the property arrays is for the host contract pointer. Application build makes sure
+	// of that.
+	init_runtime_property_values[0] = host_contract_ptr_buffer.data ();
+	int hr = coreclr_initialize (
+		application_config.android_package_name,
+		"Xamarin.Android",
+		(int)application_config.number_of_runtime_properties,
+		init_runtime_property_names,
+		const_cast<const char**>(init_runtime_property_values),
+		&clr_host,
+		&domain_id
+	);
+
+	if (FastTiming::enabled ()) [[unlikely]] {
+		internal_timing.end_event (clr_init_time_index);
+	}
+
+	// TODO: make S_OK & friends known to us
+	if (hr != 0 /* S_OK */) {
+		Helpers::abort_application (
+			LOG_DEFAULT,
+			std::format (
+				"Failed to initialize CoreCLR. Error code: {:x}",
+				static_cast<unsigned int>(hr)
+			)
+		);
+	}
+
+	abort_unless (
+		clr_host != nullptr,
+		[&hr] {
+			return detail::_format_message ("Failure to initialize CoreCLR host instance. Returned result 0x%x", static_cast<unsigned int>(hr));
+		}
+	);
+
+	struct JnienvInitializeArgs init = {};
+	init.runtimeType                                    = RuntimeTypeCoreCLR;
+	init.javaVm                                         = jvm;
+	init.env                                            = env;
+	init.logCategories                                  = log_categories;
+	init.version                                        = env->GetVersion ();
+	init.isRunningOnDesktop                             = false;
+	init.brokenExceptionTransitions                     = 0;
+	init.packageNamingPolicy                            = static_cast<int>(application_config.package_naming_policy);
+	init.boundExceptionType                             = 0; // System
+	init.jniAddNativeMethodRegistrationAttributePresent = application_config.jni_add_native_method_registration_attribute_present ? 1 : 0;
+	init.jniRemappingInUse                              = application_config.jni_remapping_replacement_type_count > 0 || application_config.jni_remapping_replacement_method_index_entry_count > 0;
+	init.marshalMethodsEnabled                          = application_config.marshal_methods_enabled;
+
+	// GC threshold is 90% of the max GREF count
+	init.grefGcThreshold                                = static_cast<int>(AndroidSystem::get_gref_gc_threshold ());
+	init.grefClass                                      = HostUtil::get_class_from_runtime_field (env, runtimeClass, "java_lang_Class", true);
+	Class_getName                                       = env->GetMethodID (init.grefClass, "getName", "()Ljava/lang/String;");
+
+	jclass lrefLoaderClass                              = env->GetObjectClass (loader);
+	init.Loader_loadClass                               = env->GetMethodID (lrefLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+	env->DeleteLocalRef (lrefLoaderClass);
+
+	init.grefLoader                                     = env->NewGlobalRef (loader);
+	init.grefIGCUserPeer                                = HostUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_IGCUserPeer", true);
+	init.grefGCUserPeerable                             = HostUtil::get_class_from_runtime_field (env, runtimeClass, "net_dot_jni_GCUserPeerable", true);
+
+	log_info (LOG_GC, "GREF GC Threshold: {}", init.grefGcThreshold);
+
+	// TODO: GC bridge to initialize here
+
+	OSBridge::initialize_on_runtime_init (env, runtimeClass);
+
+	size_t native_to_managed_index;
+	if (FastTiming::enabled ()) [[unlikely]] {
+		native_to_managed_index = internal_timing.start_event (TimingEventKind::NativeToManagedTransition);
+	}
+
+	void *delegate = nullptr;
+	log_debug (LOG_ASSEMBLY, "Creating UCO delegate to {}.RegisterJniNatives", Constants::JNIENVINIT_FULL_TYPE_NAME);
+	delegate = create_delegate (Constants::MONO_ANDROID_ASSEMBLY_NAME, Constants::JNIENVINIT_FULL_TYPE_NAME, "RegisterJniNatives"sv);
+	jnienv_register_jni_natives = reinterpret_cast<jnienv_register_jni_natives_fn> (delegate);
+	abort_unless (
+		jnienv_register_jni_natives != nullptr,
+		[] {
+			return detail::_format_message (
+				"Failed to obtain unmanaged-callers-only pointer to the %s.%s.RegisterJniNatives method.",
+				Constants::MONO_ANDROID_ASSEMBLY_NAME,
+				Constants::JNIENVINIT_FULL_TYPE_NAME
+			);
+		}
+	);
+
+	log_debug (LOG_ASSEMBLY, "Creating UCO delegate to {}.Initialize", Constants::JNIENVINIT_FULL_TYPE_NAME);
+	delegate = create_delegate (Constants::MONO_ANDROID_ASSEMBLY_NAME, Constants::JNIENVINIT_FULL_TYPE_NAME, "Initialize"sv);
+	auto initialize = reinterpret_cast<jnienv_initialize_fn> (delegate);
+	abort_unless (
+		initialize != nullptr,
+		[] {
+			return detail::_format_message (
+				"Failed to obtain unmanaged-callers-only pointer to the %s.%s.Initialize method.",
+				Constants::MONO_ANDROID_ASSEMBLY_NAME,
+				Constants::JNIENVINIT_FULL_TYPE_NAME
+			);
+		}
+	);
+
+	log_debug (LOG_DEFAULT, "Calling into managed runtime init"sv);
+	initialize (&init);
+
+	if (FastTiming::enabled ()) [[unlikely]] {
+		internal_timing.end_event (native_to_managed_index);
+		internal_timing.end_event (total_time_index);
+	}
+}
+
+void Host::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, jclass nativeClass, jstring methods) noexcept
+{
+	size_t total_time_index;
+	if (FastTiming::enabled ()) [[unlikely]] {
+		total_time_index = internal_timing.start_event (TimingEventKind::RuntimeRegister);
+	}
+
+	jsize managedType_len = env->GetStringLength (managedType);
+	const jchar *managedType_ptr = env->GetStringChars (managedType, nullptr);
+	int methods_len = env->GetStringLength (methods);
+	const jchar *methods_ptr = env->GetStringChars (methods, nullptr);
+
+	// TODO: must attach thread to the runtime here
+	jnienv_register_jni_natives (managedType_ptr, managedType_len, nativeClass, methods_ptr, methods_len);
+
+	env->ReleaseStringChars (methods, methods_ptr);
+	env->ReleaseStringChars (managedType, managedType_ptr);
+
+	if (FastTiming::enabled ()) [[unlikely]] {
+		internal_timing.end_event (total_time_index, true /* uses_more_info */);
+
+		dynamic_local_string<SENSIBLE_TYPE_NAME_LENGTH> type;
+		const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr);
+		type.assign (mt_ptr, strlen (mt_ptr));
+		env->ReleaseStringUTFChars (managedType, mt_ptr);
+
+		internal_timing.add_more_info (total_time_index, type);
+	}
+}
+
+auto Host::get_java_class_name_for_TypeManager (jclass klass) noexcept -> char*
+{
+	if (klass == nullptr || Class_getName == nullptr) {
+		return nullptr;
+	}
+
+	JNIEnv *env = OSBridge::ensure_jnienv ();
+	jstring name = reinterpret_cast<jstring> (env->CallObjectMethod (klass, Class_getName));
+	if (name == nullptr) {
+		log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast<void*>(klass));
+		return nullptr;
+	}
+
+	const char *mutf8 = env->GetStringUTFChars (name, nullptr);
+	if (mutf8 == nullptr) {
+		log_error (LOG_DEFAULT, "Failed to convert Java class name to UTF8 (out of memory?)"sv);
+		env->DeleteLocalRef (name);
+		return nullptr;
+	}
+	char *ret = strdup (mutf8);
+
+	env->ReleaseStringUTFChars (name, mutf8);
+	env->DeleteLocalRef (name);
+
+	char *dot = strchr (ret, '.');
+	while (dot != nullptr) {
+		*dot = '/';
+		dot = strchr (dot + 1, '.');
+	}
+
+	return ret;
+}
+
+auto Host::Java_JNI_OnLoad (JavaVM *vm, [[maybe_unused]] void *reserved) noexcept -> jint
+{
+	log_write (LOG_DEFAULT, LogLevel::Info, "Host OnLoad");
+	jvm = vm;
+
+	JNIEnv *env = nullptr;
+	vm->GetEnv ((void**)&env, JNI_VERSION_1_6);
+	OSBridge::initialize_on_onload (vm, env);
+
+	AndroidSystem::init_max_gref_count ();
+	return JNI_VERSION_1_6;
+}
diff --git a/src/native/clr/host/internal-pinvokes.cc b/src/native/clr/host/internal-pinvokes.cc
new file mode 100644
index 00000000000..39562e12ec5
--- /dev/null
+++ b/src/native/clr/host/internal-pinvokes.cc
@@ -0,0 +1,77 @@
+#include <host/host.hh>
+#include <host/os-bridge.hh>
+#include <host/typemap.hh>
+#include <runtime-base/internal-pinvokes.hh>
+
+using namespace xamarin::android;
+
+int _monodroid_gref_get () noexcept
+{
+	return OSBridge::get_gc_gref_count ();
+}
+
+void _monodroid_gref_log (const char *message) noexcept
+{
+}
+
+int _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, int from_writable) noexcept
+{
+	return OSBridge::_monodroid_gref_log_new (curHandle, curType, newHandle, newType, threadName, threadId, from, from_writable);
+}
+
+void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept
+{
+}
+
+const char* clr_typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept
+{
+	return TypeMapper::typemap_managed_to_java (typeName, mvid);
+}
+
+bool clr_typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept
+{
+	return TypeMapper::typemap_java_to_managed (java_type_name, assembly_name, managed_type_token_id);
+}
+
+void monodroid_log (LogLevel level, LogCategories category, const char *message) noexcept
+{
+	switch (level) {
+		case LogLevel::Verbose:
+		case LogLevel::Debug:
+			log_debug_nocheck (category, std::string_view { message });
+			break;
+
+		case LogLevel::Info:
+			log_info_nocheck (category, std::string_view { message });
+			break;
+
+		case LogLevel::Warn:
+		case LogLevel::Silent: // warn is always printed
+			log_warn (category, std::string_view { message });
+			break;
+
+		case LogLevel::Error:
+			log_error (category, std::string_view { message });
+			break;
+
+		case LogLevel::Fatal:
+			log_fatal (category, std::string_view { message });
+			break;
+
+		default:
+		case LogLevel::Unknown:
+		case LogLevel::Default:
+			log_info_nocheck (category, std::string_view { message });
+			break;
+	}
+}
+
+char* monodroid_TypeManager_get_java_class_name (jclass klass) noexcept
+{
+	return Host::get_java_class_name_for_TypeManager (klass);
+}
+
+void monodroid_free (void *ptr) noexcept
+{
+	free (ptr);
+}
diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc
new file mode 100644
index 00000000000..a2ec0492340
--- /dev/null
+++ b/src/native/clr/host/os-bridge.cc
@@ -0,0 +1,211 @@
+#include <host/host-util.hh>
+#include <host/os-bridge.hh>
+#include <runtime-base/logger.hh>
+#include <shared/cpp-util.hh>
+#include <shared/helpers.hh>
+
+using namespace xamarin::android;
+
+void OSBridge::initialize_on_onload (JavaVM *vm, JNIEnv *env) noexcept
+{
+	abort_if_invalid_pointer_argument (env, "env");
+	abort_if_invalid_pointer_argument (vm, "vm");
+
+	jvm = vm;
+	// jclass lref = env->FindClass ("java/lang/Runtime");
+	// jmethodID Runtime_getRuntime = env->GetStaticMethodID (lref, "getRuntime", "()Ljava/lang/Runtime;");
+
+	// Runtime_gc			= env->GetMethodID (lref, "gc", "()V");
+	// Runtime_instance	= lref_to_gref (env, env->CallStaticObjectMethod (lref, Runtime_getRuntime));
+	// env->DeleteLocalRef (lref);
+	// lref = env->FindClass ("java/lang/ref/WeakReference");
+	// weakrefClass = reinterpret_cast<jclass> (env->NewGlobalRef (lref));
+	// env->DeleteLocalRef (lref);
+	// weakrefCtor = env->GetMethodID (weakrefClass, "<init>", "(Ljava/lang/Object;)V");
+	// weakrefGet = env->GetMethodID (weakrefClass, "get", "()Ljava/lang/Object;");
+
+	// abort_unless (
+	// 	weakrefClass != nullptr && weakrefCtor != nullptr && weakrefGet != nullptr,
+	// 	"Failed to look up required java.lang.ref.WeakReference members"
+	// );
+}
+
+void OSBridge::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept
+{
+	abort_if_invalid_pointer_argument (env, "env");
+	GCUserPeer_class = HostUtil::get_class_from_runtime_field(env, runtimeClass, "mono_android_GCUserPeer", true);
+	GCUserPeer_ctor	 = env->GetMethodID (GCUserPeer_class, "<init>", "()V");
+	abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!");
+}
+
+auto OSBridge::lref_to_gref (JNIEnv *env, jobject lref) noexcept -> jobject
+{
+	if (lref == 0) {
+		return 0;
+	}
+
+	jobject g = env->NewGlobalRef (lref);
+	env->DeleteLocalRef (lref);
+	return g;
+}
+
+auto OSBridge::_monodroid_gref_inc () noexcept -> int
+{
+	return __sync_add_and_fetch (&gc_gref_count, 1);
+}
+
+auto OSBridge::_monodroid_gref_dec () noexcept -> int
+{
+	return __sync_sub_and_fetch (&gc_gref_count, 1);
+}
+
+[[gnu::always_inline]]
+auto OSBridge::_get_stack_trace_line_end (char *m) noexcept -> char*
+{
+	while (*m && *m != '\n') {
+		m++;
+	}
+
+	return m;
+}
+
+[[gnu::always_inline]]
+void OSBridge::_write_stack_trace (FILE *to, char *from, LogCategories category) noexcept
+{
+	char *n = const_cast<char*> (from);
+
+	char c;
+	do {
+		char *m		= n;
+		char *end	= _get_stack_trace_line_end (m);
+
+		n		= end + 1;
+		c		= *end;
+		*end	= '\0';
+		if ((category == LOG_GREF && Logger::gref_to_logcat ()) ||
+			(category == LOG_LREF && Logger::lref_to_logcat ())) {
+				log_debug (category, "{}", optional_string (m));
+		}
+
+		if (to != nullptr) {
+			fprintf (to, "%s\n", optional_string (m));
+			fflush (to);
+		}
+		*end	= c;
+	} while (c);
+}
+
+void OSBridge::_monodroid_gref_log (const char *message) noexcept
+{
+	if (Logger::gref_to_logcat ()) {
+		log_debug (LOG_GREF, "{}", optional_string (message));
+	}
+
+	if (Logger::gref_log () == nullptr) {
+		return;
+	}
+
+	fprintf (Logger::gref_log (), "%s", optional_string (message));
+	fflush (Logger::gref_log ());
+}
+
+auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, int from_writable) noexcept -> int
+{
+	int c = _monodroid_gref_inc ();
+	if ((log_categories & LOG_GREF) == 0) {
+		return c;
+	}
+
+	log_info (LOG_GREF,
+			  "+g+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})",
+			  c,
+			  gc_weak_gref_count,
+			  reinterpret_cast<void*>(curHandle),
+			  curType,
+			  reinterpret_cast<void*>(newHandle),
+			  newType,
+			  optional_string (threadName),
+			  threadId
+	);
+
+	if (Logger::gref_to_logcat ()) {
+		if (from_writable) {
+			_write_stack_trace (nullptr, const_cast<char*>(from), LOG_GREF);
+		} else {
+			log_info (LOG_GREF, "{}", optional_string (from));
+		}
+	}
+
+	if (Logger::gref_log () == nullptr) {
+		return c;
+	}
+
+	fprintf (
+		Logger::gref_log (),
+		"+g+ grefc %i gwrefc %i obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%i)\n",
+		c,
+		gc_weak_gref_count,
+		curHandle,
+		curType,
+		newHandle,
+		newType,
+		optional_string (threadName),
+		threadId
+	);
+
+	if (from_writable) {
+		_write_stack_trace (Logger::gref_log (), const_cast<char*>(from));
+	} else {
+		fprintf (Logger::gref_log (), "%s\n", from);
+	}
+
+	fflush (Logger::gref_log ());
+	return c;
+}
+
+void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept
+{
+	int c = _monodroid_gref_dec ();
+	if ((log_categories & LOG_GREF) == 0) {
+		return;
+	}
+
+	log_info (LOG_GREF,
+			  "-g- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})",
+			  c,
+			  gc_weak_gref_count,
+			  reinterpret_cast<void*>(handle),
+			  type,
+			  optional_string (threadName),
+			  threadId
+	);
+	if (Logger::gref_to_logcat ()) {
+		if (from_writable) {
+			_write_stack_trace (nullptr, const_cast<char*>(from), LOG_GREF);
+		} else {
+			log_info (LOG_GREF, "{}", optional_string (from));
+		}
+	}
+
+	if (Logger::gref_log () == nullptr) {
+		return;
+	}
+
+	fprintf (Logger::gref_log (),
+			 "-g- grefc %i gwrefc %i handle %p/%c from thread '%s'(%i)\n",
+			 c,
+			 gc_weak_gref_count,
+			 handle,
+			 type,
+			 optional_string (threadName),
+			 threadId
+	);
+
+	if (from_writable) {
+		_write_stack_trace (Logger::gref_log (), const_cast<char*>(from));
+	} else {
+		fprintf (Logger::gref_log(), "%s\n", optional_string (from));
+	}
+
+	fflush (Logger::gref_log ());
+}
diff --git a/src/native/clr/host/pinvoke-override.cc b/src/native/clr/host/pinvoke-override.cc
new file mode 100644
index 00000000000..d1467e4eda1
--- /dev/null
+++ b/src/native/clr/host/pinvoke-override.cc
@@ -0,0 +1,105 @@
+#include <host/host.hh>
+#include <host/pinvoke-override.hh>
+#include <runtime-base/internal-pinvokes.hh>
+
+using namespace xamarin::android;
+
+#include "pinvoke-tables.include"
+
+[[gnu::flatten]]
+auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void*
+{
+	if (library_name == nullptr || entrypoint_name == nullptr) {
+		return nullptr;
+	}
+
+	hash_t library_name_hash = xxhash::hash (library_name, strlen (library_name));
+	hash_t entrypoint_hash = xxhash::hash (entrypoint_name, strlen (entrypoint_name));
+
+	if (library_name_hash == java_interop_library_hash || library_name_hash == xa_internal_api_library_hash || library_name_hash == android_liblog_library_hash) {
+		PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, internal_pinvokes.data (), internal_pinvokes_count);
+
+		if (entry == nullptr) [[unlikely]] {
+			log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '{} @ {}' (hash: {:x}) not found in compile-time map.",
+									 optional_string (library_name), optional_string (entrypoint_name), entrypoint_hash);
+			log_fatal (LOG_ASSEMBLY, "compile-time map contents:"sv);
+			for (size_t i = 0uz; i < internal_pinvokes_count; i++) {
+				PinvokeEntry const& e = internal_pinvokes[i];
+				log_fatal (LOG_ASSEMBLY, "\t'{}'={:p} (hash: {:x})", optional_string (e.name), e.func, e.hash);
+			}
+			Helpers::abort_application (
+				LOG_ASSEMBLY,
+				std::format (
+					"Failure handling a p/invoke request for '{}'@'{}'",
+					optional_string (entrypoint_name),
+					optional_string (library_name)
+				)
+			);
+		}
+
+		return entry->func;
+	}
+
+	// The order of statements below should be kept in the descending probability of occurrence order (as much as
+	// possible, of course). `libSystem.Native` is requested during early startup for each MAUI app, so its
+	// probability is higher, just as it's more likely that `libSystem.Security.Cryptography.Android` will be used
+	// in an app rather than `libSystem.IO.Compression.Native`
+	void **dotnet_dso_handle; // Set to a non-null value only for dotnet shared libraries
+	if (library_name_hash == system_native_library_hash) {
+		dotnet_dso_handle = &system_native_library_handle;
+	} else if (library_name_hash == system_security_cryptography_native_android_library_hash) {
+		dotnet_dso_handle = &system_security_cryptography_native_android_library_handle;
+	} else if (library_name_hash == system_io_compression_native_library_hash) {
+		dotnet_dso_handle = &system_io_compression_native_library_handle;
+	} else if (library_name_hash == system_globalization_native_library_hash) {
+		dotnet_dso_handle = &system_globalization_native_library_handle;
+	} else {
+		dotnet_dso_handle = nullptr;
+	}
+
+	if (dotnet_dso_handle != nullptr) {
+		PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, dotnet_pinvokes.data (), dotnet_pinvokes_count);
+		if (entry != nullptr) {
+			if (entry->func != nullptr) {
+				return entry->func;
+			}
+
+			load_library_entry (library_name, entrypoint_name, *entry, dotnet_dso_handle);
+			if (entry->func == nullptr) {
+				log_fatal (LOG_ASSEMBLY, "Failed to load symbol '{}' from shared library '{}'",
+										 optional_string (entrypoint_name), optional_string (library_name));
+				return nullptr; // let Mono deal with the fallout
+			}
+
+			return entry->func;
+		}
+
+		// It's possible we don't have an entry for some `dotnet` p/invoke, fall back to the slow path below
+		log_debug (
+			LOG_ASSEMBLY,
+			"Symbol '{}' in library '{}' not found in the generated tables, falling back to slow path",
+			optional_string (entrypoint_name),
+			optional_string (library_name)
+		);
+
+		// This is temporary, to catch p/invokes we might be missing that are used in the default templates
+		Helpers::abort_application (
+			LOG_ASSEMBLY,
+			std::format (
+				"Missing pinvoke {}@{}",
+				optional_string (entrypoint_name),
+				optional_string (library_name)
+			)
+		);
+	}
+
+	return handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash);
+}
+
+const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept
+{
+	log_debug (LOG_ASSEMBLY, "clr_pinvoke_override (\"{}\", \"{}\")", library_name, entry_point_name);
+	void *ret = PinvokeOverride::monodroid_pinvoke_override (library_name, entry_point_name);
+	log_debug (LOG_DEFAULT, "p/invoke {}found", ret == nullptr ? "not"sv : ""sv);
+	return ret;
+}
diff --git a/src/native/clr/host/pinvoke-tables.include b/src/native/clr/host/pinvoke-tables.include
new file mode 100644
index 00000000000..73d07f125c6
--- /dev/null
+++ b/src/native/clr/host/pinvoke-tables.include
@@ -0,0 +1,1024 @@
+//
+// Autogenarated file. DO NOT EDIT.
+//
+// To regenerate run ../../../../build-tools/scripts/generate-pinvoke-tables.sh on Linux or macOS
+// A compiler with support for C++20 ranges is required
+//
+
+#include <array>
+#include <cstdint>
+
+namespace {
+#if INTPTR_MAX == INT64_MAX
+	//64-bit internal p/invoke table
+	std::array<PinvokeEntry, 10> internal_pinvokes {{
+		{0x4310c1531ddddc14, "__android_log_print", reinterpret_cast<void*>(&__android_log_print)},
+		{0x4b1956138764939a, "_monodroid_gref_log_new", reinterpret_cast<void*>(&_monodroid_gref_log_new)},
+		{0x9187e6bc6294cacf, "clr_typemap_managed_to_java", reinterpret_cast<void*>(&clr_typemap_managed_to_java)},
+		{0x9a946dfe9916a942, "clr_typemap_java_to_managed", reinterpret_cast<void*>(&clr_typemap_java_to_managed)},
+		{0xa7f58f3ee428cc6b, "_monodroid_gref_log_delete", reinterpret_cast<void*>(&_monodroid_gref_log_delete)},
+		{0xae3df96dda0143bd, "_monodroid_gref_log", reinterpret_cast<void*>(&_monodroid_gref_log)},
+		{0xb8306f71b963cd3d, "monodroid_log", reinterpret_cast<void*>(&monodroid_log)},
+		{0xd1e121b94ea63f2e, "_monodroid_gref_get", reinterpret_cast<void*>(&_monodroid_gref_get)},
+		{0xd5151b00eb33d85e, "monodroid_TypeManager_get_java_class_name", reinterpret_cast<void*>(&monodroid_TypeManager_get_java_class_name)},
+		{0xf41c48df6f9be476, "monodroid_free", reinterpret_cast<void*>(&monodroid_free)},
+	}};
+
+	//64-bit DotNet p/invoke table
+	std::array<PinvokeEntry, 477> dotnet_pinvokes {{
+		{0x99f2ee02463000, "CompressionNative_Crc32", nullptr},
+		{0xb38afc8bfe830b, "SystemNative_Bind", nullptr},
+		{0x190fe65d8736dcb, "SystemNative_TryGetIPPacketInformation", nullptr},
+		{0x1c8b86562ad5772, "SystemNative_Receive", nullptr},
+		{0x202543f28ecaf06, "SystemNative_Abort", nullptr},
+		{0x25abeafa88904a2, "SystemNative_SetPosixSignalHandler", nullptr},
+		{0x33158212a812caf, "SystemNative_GetEstimatedTcpConnectionCount", nullptr},
+		{0x3511e36d0a6c1b5, "SystemNative_LockFileRegion", nullptr},
+		{0x375a0e90c77ca35, "AndroidCryptoNative_EcKeyCreateByExplicitParameters", nullptr},
+		{0x37b9dd562235e42, "SystemNative_MSync", nullptr},
+		{0x3a5df4793dd3230, "SystemNative_INotifyInit", nullptr},
+		{0x3d24547fa4fc31b, "SystemNative_GetUInt64OSThreadId", nullptr},
+		{0x410f8526b1edfc3, "GlobalizationNative_GetLocaleInfoInt", nullptr},
+		{0x47302bd7e277183, "AndroidCryptoNative_X509GetCertificateForPrivateKeyEntry", nullptr},
+		{0x581df5b0a00c422, "SystemNative_SetRLimit", nullptr},
+		{0x598db66ca39c41f, "AndroidCryptoNative_EcKeyUpRef", nullptr},
+		{0x5b5ab451ff38f8e, "SystemNative_GetMaximumAddressSize", nullptr},
+		{0x656cac62ccc9e3c, "AndroidCryptoNative_X509GetContentType", nullptr},
+		{0x6861b5336291d12, "SystemNative_PathConf", nullptr},
+		{0x690c4347972024f, "AndroidCryptoNative_Aes256Gcm", nullptr},
+		{0x6a1f4deffa02c30, "SystemNative_LowLevelMonitor_Acquire", nullptr},
+		{0x7b5579ab0499b1f, "AndroidCryptoNative_RsaSize", nullptr},
+		{0x7ce8a9b967dd269, "SystemNative_Read", nullptr},
+		{0x7f0e1227c9c0225, "CryptoNative_EvpMdCtxDestroy", nullptr},
+		{0x8352ae4bba2b83b, "SystemNative_SetSendTimeout", nullptr},
+		{0x98bd27a7461321d, "SystemNative_Dup", nullptr},
+		{0x9a39fbf59eed9f9, "CryptoNative_EvpSha1", nullptr},
+		{0xa4aeeaff9ca2d10, "BrotliDecoderDecompressStream", nullptr},
+		{0xa906c14ca5834bc, "SystemNative_GetEUid", nullptr},
+		{0xac9f9c1abb62a92, "SystemNative_Log", nullptr},
+		{0xadb2441bcfcdfe9, "SystemNative_CreateThread", nullptr},
+		{0xafbf5c69d1badc0, "SystemNative_SetTerminalInvalidationHandler", nullptr},
+		{0xba897b7abe67b16, "SystemNative_FcntlSetPipeSz", nullptr},
+		{0xc305c22ce7ab8a0, "SystemNative_SetSockOpt", nullptr},
+		{0xc79e924361c15ca, "SystemNative_RealPath", nullptr},
+		{0xcaba893801c6a6f, "AndroidCryptoNative_Aes256Ecb", nullptr},
+		{0xcbe6d3d22131194, "AndroidCryptoNative_SetRsaParameters", nullptr},
+		{0xe7e93cf9237e1f2, "GlobalizationNative_ToAscii", nullptr},
+		{0xef8dd67e25bac53, "SystemNative_GetWindowSize", nullptr},
+		{0xfa0899cf8d00a87, "SystemNative_MkDir", nullptr},
+		{0xfe7079441ac127e, "SystemNative_CreateSocketEventPort", nullptr},
+		{0x1027786cdd9a3e9c, "AndroidCryptoNative_Aes192Cbc", nullptr},
+		{0x10d733abd1fd94bb, "SystemNative_TryChangeSocketEventRegistration", nullptr},
+		{0x114b8384553f5418, "SystemNative_GetSystemTimeAsTicks", nullptr},
+		{0x119a38c3e288a233, "SystemNative_SNPrintF_1S", nullptr},
+		{0x11b6f4f0aafeda95, "SystemNative_LowLevelMonitor_TimedWait", nullptr},
+		{0x11cc73f2926d4064, "SystemNative_ConfigureTerminalForChildProcess", nullptr},
+		{0x121bc483ac26f5f8, "SystemNative_GetGroupName", nullptr},
+		{0x12d65f9f65b01497, "SystemNative_GetRawSockOpt", nullptr},
+		{0x12eaf09505dc19fd, "SystemNative_FStat", nullptr},
+		{0x13577369f5ec4b0a, "SystemNative_GetActiveTcpConnectionInfos", nullptr},
+		{0x1399413d8a7d9dd8, "SystemNative_GetAddressFamily", nullptr},
+		{0x13a1c2de7fb2519f, "SystemNative_CloseSocketEventPort", nullptr},
+		{0x146cd1dc4fb2ba58, "SystemNative_LChflagsCanSetHiddenFlag", nullptr},
+		{0x14b7e3527b60e83f, "CryptoNative_ErrErrorStringN", nullptr},
+		{0x15bd710d3a9b3b0c, "CryptoNative_EvpDigestFinalEx", nullptr},
+		{0x176e22ea7c580dae, "SystemNative_ReadDirR", nullptr},
+		{0x18580a4592ed1ea6, "GlobalizationNative_GetSortKey", nullptr},
+		{0x185f5d25252c3c72, "SystemNative_FAllocate", nullptr},
+		{0x18d6b5e9fec9b0dc, "SystemNative_Connectx", nullptr},
+		{0x18f7da5f584b5b59, "SystemNative_PReadV", nullptr},
+		{0x1948a0cf88329c2f, "SystemNative_HandleNonCanceledPosixSignal", nullptr},
+		{0x1ac95b02f23933cc, "SystemNative_CanGetHiddenFlag", nullptr},
+		{0x1d1bb0528d517729, "AndroidCryptoNative_SSLGetSupportedProtocols", nullptr},
+		{0x1d4dcbc06728e689, "SystemNative_Close", nullptr},
+		{0x1d6d4278ffbbab77, "SystemNative_Pipe", nullptr},
+		{0x1d8d6a688fc5bfb3, "SystemNative_SendFile", nullptr},
+		{0x1e6228e955989698, "AndroidCryptoNative_EcKeyCreateByOid", nullptr},
+		{0x1e8edcc515cd23f9, "CryptoNative_EvpDigestOneShot", nullptr},
+		{0x1f1c61a157636aad, "SystemNative_Stat", nullptr},
+		{0x1f45ac9d3c6b1554, "AndroidCryptoNative_SSLStreamGetCipherSuite", nullptr},
+		{0x1f72f52873ced9c9, "GlobalizationNative_InitOrdinalCasingPage", nullptr},
+		{0x1f7d2360a1cdcbff, "AndroidCryptoNative_SSLStreamCreate", nullptr},
+		{0x1f849e45a3014a9f, "SystemNative_GetIPv6Address", nullptr},
+		{0x1f9361fc7b624c1b, "SystemNative_LowLevelMonitor_Wait", nullptr},
+		{0x205a31e661496019, "CryptoNative_ErrGetErrorAlloc", nullptr},
+		{0x20784dcc7e9cee75, "BrotliGetTransforms", nullptr},
+		{0x2178ba302d0c5f1c, "GlobalizationNative_GetCalendars", nullptr},
+		{0x218fce505a140c55, "AndroidCryptoNative_EcDsaVerify", nullptr},
+		{0x2291e0ba4e1b55b0, "SystemNative_LStat", nullptr},
+		{0x23ac2a4c4d1c744e, "AndroidCryptoNative_X509ChainGetCertificateCount", nullptr},
+		{0x24f840f903a26ded, "SystemNative_ConvertErrorPalToPlatform", nullptr},
+		{0x24ff74e427d0626e, "SystemNative_GetErrNo", nullptr},
+		{0x254905036a0061cf, "SystemNative_CreateSocketEventBuffer", nullptr},
+		{0x255c4a2e297fd9f5, "SystemNative_INotifyAddWatch", nullptr},
+		{0x267c94097a3bf1f3, "AndroidCryptoNative_CipherDestroy", nullptr},
+		{0x27944922cd8283ca, "CryptoNative_EvpSha384", nullptr},
+		{0x2795a01c2c64aea1, "CryptoNative_HmacReset", nullptr},
+		{0x27f3d9266af2b315, "SystemNative_GetIPv4Address", nullptr},
+		{0x2925953889c48cab, "SystemNative_CreateNetworkChangeListenerSocket", nullptr},
+		{0x2a49948ae20571cb, "SystemNative_SchedGetAffinity", nullptr},
+		{0x2b45d7cdf6e8e0c7, "AndroidCryptoNative_X509StoreDeleteEntry", nullptr},
+		{0x2c352dd7c367e438, "CryptoNative_EvpSha512", nullptr},
+		{0x2c7e5e179cc917cb, "AndroidCryptoNative_DsaSizeSignature", nullptr},
+		{0x2c8da1192c5d7d2b, "SystemNative_FLock", nullptr},
+		{0x2d64b1ac218cf29e, "SystemNative_AlignedRealloc", nullptr},
+		{0x2e1102c297588e10, "BrotliEncoderDestroyInstance", nullptr},
+		{0x2e429d96a9fc92bd, "SystemNative_InitializeTerminalAndSignalHandling", nullptr},
+		{0x2fdcf708ff792105, "AndroidCryptoNative_SSLStreamVerifyHostname", nullptr},
+		{0x301c465c1ac0adf9, "SystemNative_MProtect", nullptr},
+		{0x307db94ae9f929e5, "CryptoNative_GetMaxMdSize", nullptr},
+		{0x31027564deeb71b0, "AndroidCryptoNative_Aes128Cbc", nullptr},
+		{0x32e594690358a960, "GlobalizationNative_GetLocaleInfoString", nullptr},
+		{0x3319a5483b3cc1fc, "SystemNative_GetRLimit", nullptr},
+		{0x3424ffcb69ecef57, "SystemNative_Unlink", nullptr},
+		{0x346a9bb11364833c, "SystemNative_DrainAutoreleasePool", nullptr},
+		{0x35169e67cc0f8529, "SystemNative_GetIPv6MulticastOption", nullptr},
+		{0x359205b4a10fa780, "SystemNative_LowLevelMonitor_Destroy", nullptr},
+		{0x35c1fa8dffcbbd8c, "CryptoNative_EvpDigestReset", nullptr},
+		{0x36128eed665b1923, "SystemNative_ShmUnlink", nullptr},
+		{0x364dcf65ae63adff, "SystemNative_GetSocketErrorOption", nullptr},
+		{0x3757b327944abb54, "SystemNative_EnablePosixSignalHandling", nullptr},
+		{0x38b4bd21127ceffd, "SystemNative_StrErrorR", nullptr},
+		{0x38c7de719e8ae69d, "SystemNative_RmDir", nullptr},
+		{0x391bbbb9bbde4455, "SystemNative_SetIPv4MulticastOption", nullptr},
+		{0x3a7245f3ea476bf7, "SystemNative_SNPrintF", nullptr},
+		{0x3ae92e4198427b0d, "SystemNative_ReadLink", nullptr},
+		{0x3e0de839e6cfa6e5, "SystemNative_Accept", nullptr},
+		{0x3e7cf9a4789a31c7, "SystemNative_FChflags", nullptr},
+		{0x3f19a16a3230b551, "AndroidCryptoNative_ChaCha20Poly1305", nullptr},
+		{0x3f49b6278f04ae84, "SystemNative_Disconnect", nullptr},
+		{0x3fba15600bf0f229, "SystemNative_SetEUid", nullptr},
+		{0x401935ffc3454bb1, "AndroidCryptoNative_X509PublicKey", nullptr},
+		{0x403e1bc0b3baba84, "CompressionNative_Inflate", nullptr},
+		{0x40bfa1211f5f6f9c, "AndroidCryptoNative_EcKeyGetCurveName", nullptr},
+		{0x40d61d78487edb08, "GlobalizationNative_GetICUVersion", nullptr},
+		{0x41b6e7f32da99fa9, "AndroidCryptoNative_X509ChainDestroyContext", nullptr},
+		{0x41c169fb0e30a390, "AndroidCryptoNative_X509ChainGetErrorCount", nullptr},
+		{0x41c1f2c9153639af, "SystemNative_FUTimens", nullptr},
+		{0x420718c398131a55, "AndroidCryptoNative_SSLStreamGetProtocol", nullptr},
+		{0x42339dd2717504d9, "SystemNative_GetLingerOption", nullptr},
+		{0x42783107bf2935ec, "SystemNative_FreeHostEntry", nullptr},
+		{0x42eb0578a9d62b78, "SystemNative_GetFormatInfoForMountPoint", nullptr},
+		{0x4360eb8a25122eee, "GlobalizationNative_StartsWith", nullptr},
+		{0x43741165a5ba60d5, "AndroidCryptoNative_CipherUpdateAAD", nullptr},
+		{0x44ccb27979f980ce, "SystemNative_AlignedAlloc", nullptr},
+		{0x44f1a5c46033eec2, "SystemNative_SysLog", nullptr},
+		{0x469898c8d892af83, "BrotliEncoderCompress", nullptr},
+		{0x483b434d7b089c7e, "SystemNative_Write", nullptr},
+		{0x4845e1c76265acc9, "AndroidCryptoNative_X509StoreEnumerateCertificates", nullptr},
+		{0x484a3a445bdb14fc, "SystemNative_GetOSArchitecture", nullptr},
+		{0x4909639a9d87bdb5, "SystemNative_AlignedFree", nullptr},
+		{0x49e3ba95feb79c6c, "SystemNative_SetAddressFamily", nullptr},
+		{0x4a7272ac9d117f2d, "AndroidCryptoNative_EcKeyDestroy", nullptr},
+		{0x4b00795bbeea6f60, "SystemNative_SetIPv6Address", nullptr},
+		{0x4bd4b1c0803c8c55, "GlobalizationNative_GetLocaleName", nullptr},
+		{0x4be7ceca50f3298c, "SystemNative_LowLevelMonitor_Create", nullptr},
+		{0x4bec4a1d7dfd4cf7, "SystemNative_GetUnixRelease", nullptr},
+		{0x4bfff22801b209ca, "SystemNative_LChflags", nullptr},
+		{0x4c22cc4f2b1dab26, "SystemNative_SetPriority", nullptr},
+		{0x4c5d96426f92c29d, "CryptoNative_HmacUpdate", nullptr},
+		{0x4d6361e5095cff36, "AndroidCryptoNative_DsaSign", nullptr},
+		{0x4d74053b37e582fa, "AndroidCryptoNative_X509ChainCreateContext", nullptr},
+		{0x4f22643b9509cc12, "GlobalizationNative_IsNormalized", nullptr},
+		{0x501daf7e3a890220, "AndroidCryptoNative_X509ChainBuild", nullptr},
+		{0x507983f11ffec7a8, "GlobalizationNative_GetTimeZoneDisplayName", nullptr},
+		{0x509ff12da4e77259, "SystemNative_GetSocketAddressSizes", nullptr},
+		{0x523240c01d14ad50, "SystemNative_GetPeerID", nullptr},
+		{0x52794f1118d32f08, "SystemNative_GetUnixVersion", nullptr},
+		{0x52fc107ebdb6fcc7, "AndroidCryptoNative_X509StoreRemoveCertificate", nullptr},
+		{0x5381564d2c06c0a3, "SystemNative_SysConf", nullptr},
+		{0x54ec3421ab70a40a, "Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate", nullptr},
+		{0x556bc89d2d4dfc85, "SystemNative_GetDeviceIdentifiers", nullptr},
+		{0x558250d199e906bb, "CryptoNative_ErrReasonErrorString", nullptr},
+		{0x5592a052ceb4caf6, "SystemNative_GetProcessPath", nullptr},
+		{0x55fe2620f63d83d8, "SystemNative_SetDelayedSigChildConsoleConfigurationHandler", nullptr},
+		{0x56e982948d00f10d, "GlobalizationNative_IndexOf", nullptr},
+		{0x574d77a68ec3e488, "SystemNative_GetEnv", nullptr},
+		{0x5755d1cd0c158620, "BrotliDecoderSetParameter", nullptr},
+		{0x580dda20ac9e83c6, "BrotliEncoderSetParameter", nullptr},
+		{0x583db0344a1cd715, "SystemNative_GetActiveUdpListeners", nullptr},
+		{0x5908581fe73717f0, "SystemNative_InterfaceNameToIndex", nullptr},
+		{0x5916efc3e1e49137, "CryptoNative_ErrPeekError", nullptr},
+		{0x5a114024ecd1162c, "CryptoNative_EvpDigestUpdate", nullptr},
+		{0x5a305cf2a314d6a6, "SystemNative_FTruncate", nullptr},
+		{0x5a337d9cc7d8bcfd, "CryptoNative_HmacCurrent", nullptr},
+		{0x5d503db70d17dad2, "BrotliEncoderIsFinished", nullptr},
+		{0x5dd1d1d024378765, "CryptoNative_EvpMdSize", nullptr},
+		{0x5e53b688fede3216, "SystemNative_GetControlCharacters", nullptr},
+		{0x5fa62856bdbba9c0, "SystemNative_GetPort", nullptr},
+		{0x5fd29ac523ff6e3d, "AndroidCryptoNative_SSLStreamRelease", nullptr},
+		{0x5ffae3c8023a80b8, "AndroidCryptoNative_SSLStreamGetPeerCertificate", nullptr},
+		{0x600b4418896f7808, "SystemNative_Exit", nullptr},
+		{0x6089f0c8112eb3d9, "SystemNative_InitializeConsoleBeforeRead", nullptr},
+		{0x613307e537d462db, "SystemNative_GetReadDirRBufferSize", nullptr},
+		{0x61bacd7170fd8c9b, "SystemNative_SchedSetAffinity", nullptr},
+		{0x61f3ce1b18b20d6f, "SystemNative_GetNativeIPInterfaceStatistics", nullptr},
+		{0x62351df42d842942, "SystemNative_GetSignalForBreak", nullptr},
+		{0x635327a9b09a910d, "GlobalizationNative_NormalizeString", nullptr},
+		{0x6393d30aceaa6df2, "SystemNative_PWriteV", nullptr},
+		{0x6448f0806bd3a338, "SystemNative_FreeEnviron", nullptr},
+		{0x648a9b317bc64fe0, "AndroidCryptoNative_RsaGenerateKeyEx", nullptr},
+		{0x650eddee76c6b8da, "SystemNative_GetHostName", nullptr},
+		{0x652badfba5d61929, "SystemNative_FcntlSetFD", nullptr},
+		{0x66e049fe27bf91ea, "AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration", nullptr},
+		{0x6792c0a7ea5d19c9, "BrotliEncoderTakeOutput", nullptr},
+		{0x67a8868ef592a3fd, "AndroidCryptoNative_SSLStreamShutdown", nullptr},
+		{0x67a9b5bbce322f8c, "AndroidCryptoNative_Des3Cbc", nullptr},
+		{0x67d2cd86792b1d0c, "SystemNative_Realloc", nullptr},
+		{0x67e9d60481f4be06, "SystemNative_PlatformSupportsDualModeIPv4PacketInfo", nullptr},
+		{0x68df81a8fb5bf442, "SystemNative_GetSockOpt", nullptr},
+		{0x68f3fe6083c0355b, "SystemNative_GetLoadLibraryError", nullptr},
+		{0x69ad99fac0467f64, "SystemNative_Link", nullptr},
+		{0x6a59d9242cd31785, "AndroidCryptoNative_RsaPrivateDecrypt", nullptr},
+		{0x6ac3aeecfc75bfad, "GlobalizationNative_GetSortVersion", nullptr},
+		{0x6b9097385aa77917, "SystemNative_FSync", nullptr},
+		{0x6b9bce16ba8e845f, "SystemNative_Malloc", nullptr},
+		{0x6bc18fbbbf267e2a, "SystemNative_ReceiveSocketError", nullptr},
+		{0x6d566e1f6e5a2d8f, "BrotliDefaultAllocFunc", nullptr},
+		{0x6dbd90e9cc86310b, "AndroidCryptoNative_CipherFinalEx", nullptr},
+		{0x6dfd40c2dd0d7382, "AndroidCryptoNative_RsaUpRef", nullptr},
+		{0x6e2c1caff08e6e2d, "SystemNative_ReadStdin", nullptr},
+		{0x6ee05d5e8650e56c, "SystemNative_DisablePosixSignalHandling", nullptr},
+		{0x6f990f1f7bc80630, "AndroidCryptoNative_RsaCreate", nullptr},
+		{0x70f907b97d3fe059, "AndroidCryptoNative_Aes192Ccm", nullptr},
+		{0x7150f0eb40797bb3, "AndroidCryptoNative_SSLStreamCreateWithCertificates", nullptr},
+		{0x724820d307055ed1, "CryptoNative_HmacFinal", nullptr},
+		{0x729afe37cdb8ae8f, "SystemNative_Connect", nullptr},
+		{0x730ae9a7469a7321, "SystemNative_GetAllMountPoints", nullptr},
+		{0x7356b141407d261e, "AndroidCryptoNative_EcdhDeriveKey", nullptr},
+		{0x742da00b2dbf435d, "SystemNative_LoadLibrary", nullptr},
+		{0x74ec4a8d869776ad, "AndroidCryptoNative_Aes128Ccm", nullptr},
+		{0x7559feb379d38da5, "SystemNative_GetTimeZoneData", nullptr},
+		{0x758dfbf057da0da0, "AndroidCryptoNative_DsaSignatureFieldSize", nullptr},
+		{0x77ca6a148e5a51d9, "GlobalizationNative_IanaIdToWindowsId", nullptr},
+		{0x7975d1d7029cf1a3, "AndroidCryptoNative_Aes128Gcm", nullptr},
+		{0x79f5c24afbd04af1, "AndroidCryptoNative_Aes256Cbc", nullptr},
+		{0x7a37e0d077f2dfe5, "AndroidCryptoNative_DsaGenerateKey", nullptr},
+		{0x7a4d912694906c9c, "GlobalizationNative_ToUnicode", nullptr},
+		{0x7af1f52a7a632e95, "BrotliDecoderTakeOutput", nullptr},
+		{0x7d5273ad530e7298, "AndroidCryptoNative_X509StoreOpenDefault", nullptr},
+		{0x7d7ee4bce74d4de9, "SystemNative_GetDomainSocketSizes", nullptr},
+		{0x7e1766c6df3ad261, "SystemNative_MUnmap", nullptr},
+		{0x7e4bdf46d4ff9f11, "SystemNative_MkNod", nullptr},
+		{0x7e5fa2f70891c7fe, "GlobalizationNative_ChangeCaseTurkish", nullptr},
+		{0x7ec328b6ba9eab8a, "SystemNative_WaitForSocketEvents", nullptr},
+		{0x7fa96d0284954375, "AndroidCryptoNative_X509Decode", nullptr},
+		{0x80ef5040fdcc248d, "BrotliEncoderMaxCompressedSize", nullptr},
+		{0x813bedf08c3388d4, "AndroidCryptoNative_Aes128Cfb8", nullptr},
+		{0x84c8a7489b37fea0, "SystemNative_GetPlatformSignalNumber", nullptr},
+		{0x84cc0301870c37ce, "AndroidCryptoNative_SSLStreamSetTargetHost", nullptr},
+		{0x8502eeba98158e79, "SystemNative_FcntlSetIsNonBlocking", nullptr},
+		{0x8530d37777969db6, "SystemNative_SetKeypadXmit", nullptr},
+		{0x85d0033bc38bb4bb, "SystemNative_MAdvise", nullptr},
+		{0x868e09dc7dfea364, "AndroidCryptoNative_RsaSignPrimitive", nullptr},
+		{0x870191ad244b8069, "AndroidCryptoNative_RegisterRemoteCertificateValidationCallback", nullptr},
+		{0x87019b7831c0c34c, "AndroidCryptoNative_Aes192Gcm", nullptr},
+		{0x87c447e7f873cff0, "AndroidCryptoNative_X509ChainValidate", nullptr},
+		{0x889350f209555ecb, "SystemNative_MkdTemp", nullptr},
+		{0x88a08b60b80c70cc, "SystemNative_FChMod", nullptr},
+		{0x88cfeefc903f9d60, "CryptoNative_EvpDigestCurrent", nullptr},
+		{0x8bcabce135063bed, "SystemNative_OpenDir", nullptr},
+		{0x8df448aee6e8fa5e, "SystemNative_WaitPidExitedNoHang", nullptr},
+		{0x8e96cb02418947cc, "SystemNative_FcntlGetPipeSz", nullptr},
+		{0x8fb6ed14ee0256bc, "SystemNative_GetTimestamp", nullptr},
+		{0x8ffe2d950d138c01, "SystemNative_SchedGetCpu", nullptr},
+		{0x9039632237d70ae7, "AndroidCryptoNative_NewGlobalReference", nullptr},
+		{0x9161ade1206fd86e, "AndroidCryptoNative_Aes256Cfb128", nullptr},
+		{0x9167a072639a7c95, "AndroidCryptoNative_Aes256Ccm", nullptr},
+		{0x91f065ec0d3aec55, "AndroidCryptoNative_X509StoreAddCertificateWithPrivateKey", nullptr},
+		{0x93a8bec488055608, "SystemNative_GetPwNamR", nullptr},
+		{0x95a0e2fc5c0cb49e, "AndroidCryptoNative_SSLStreamSetApplicationProtocols", nullptr},
+		{0x95a4cb8563cc6b14, "SystemNative_ShmOpen", nullptr},
+		{0x9856fa59ed936b73, "SystemNative_GetSid", nullptr},
+		{0x996ada1c038aabba, "SystemNative_MksTemps", nullptr},
+		{0x9991a277809ef205, "AndroidCryptoNative_SSLStreamGetApplicationProtocol", nullptr},
+		{0x99a840c495204202, "SystemNative_GetBytesAvailable", nullptr},
+		{0x99e3660fc483d7be, "CryptoNative_GetRandomBytes", nullptr},
+		{0x9aa9eaee3dd8b23b, "SystemNative_GetIPv4MulticastOption", nullptr},
+		{0x9aaaad33b28af82f, "SystemNative_SetSignalForBreak", nullptr},
+		{0x9aab07f824659d3e, "AndroidCryptoNative_DsaKeyCreateByExplicitParameters", nullptr},
+		{0x9c3e8b890033819a, "SystemNative_FcntlCanGetSetPipeSz", nullptr},
+		{0x9c832cd7fcbf2de0, "SystemNative_MkFifo", nullptr},
+		{0x9d2cb31282abd3d9, "SystemNative_GetNetworkInterfaces", nullptr},
+		{0x9e25ebf4f61cc299, "SystemNative_ChDir", nullptr},
+		{0x9e79166979634030, "AndroidCryptoNative_CipherSetKeyAndIV", nullptr},
+		{0x9edddf30d660eff4, "AndroidCryptoNative_Aes192Ecb", nullptr},
+		{0x9fb01da1222e905a, "SystemNative_IsATty", nullptr},
+		{0xa193402ff5140ac1, "GlobalizationNative_GetCalendarInfo", nullptr},
+		{0xa1e881a63614507e, "SystemNative_INotifyRemoveWatch", nullptr},
+		{0xa2254fea4d8b6909, "SystemNative_MMap", nullptr},
+		{0xa272b5349013d9ef, "CryptoNative_EvpSha256", nullptr},
+		{0xa2d7790a850024c0, "SystemNative_GetNumRoutes", nullptr},
+		{0xa302613a430248b8, "SystemNative_GetGroups", nullptr},
+		{0xa308025a784497df, "AndroidCryptoNative_SSLStreamSetEnabledProtocols", nullptr},
+		{0xa56532a23755cd87, "SystemNative_StdinReady", nullptr},
+		{0xa56954e28eb9a9c9, "AndroidCryptoNative_Des3Cfb8", nullptr},
+		{0xa57e18f82abd5958, "BrotliDecoderDestroyInstance", nullptr},
+		{0xa5eda72b95fe78c3, "AndroidCryptoNative_X509ChainGetErrors", nullptr},
+		{0xa831a683f743e417, "GlobalizationNative_WindowsIdToIanaId", nullptr},
+		{0xa89b70c38d3ba079, "CryptoNative_HmacCreate", nullptr},
+		{0xa89ec9958d999483, "SystemNative_GetCwd", nullptr},
+		{0xa8bdc3e7ee898dfc, "SystemNative_Shutdown", nullptr},
+		{0xa93eb533acf7564d, "AndroidCryptoNative_DesEcb", nullptr},
+		{0xa94b1cf083978da9, "CryptoNative_EvpMdCtxCopyEx", nullptr},
+		{0xa961e8db31830e16, "AndroidCryptoNative_Aes192Cfb8", nullptr},
+		{0xaa8f0f87ae474ffe, "AndroidCryptoNative_SSLStreamCreateWithKeyStorePrivateKeyEntry", nullptr},
+		{0xabdcf2f74d210f35, "SystemNative_GetCryptographicallySecureRandomBytes", nullptr},
+		{0xac11eab9d9c31b01, "SystemNative_UTimensat", nullptr},
+		{0xac5c6a70d140a4bf, "GlobalizationNative_GetLocaleTimeFormat", nullptr},
+		{0xac7725c652a5fb5b, "SystemNative_CopyFile", nullptr},
+		{0xad1a2d6575cdd4e3, "AndroidCryptoNative_SSLStreamWrite", nullptr},
+		{0xad228cdc4edb11d6, "SystemNative_CloseDir", nullptr},
+		{0xadc6889903a2d6f4, "SystemNative_Rename", nullptr},
+		{0xae320903718eb45d, "SystemNative_MapTcpState", nullptr},
+		{0xae82e9ceae24192d, "AndroidCryptoNative_Pbkdf2", nullptr},
+		{0xaf72b94c4acee897, "BrotliDecoderHasMoreOutput", nullptr},
+		{0xaf9706efc72c3904, "SystemNative_SetIPv6MulticastOption", nullptr},
+		{0xafd9f6338cdbadd4, "SystemNative_GetHostEntryForName", nullptr},
+		{0xafe3d21bbaa71464, "CompressionNative_DeflateEnd", nullptr},
+		{0xb0b66a7145de350d, "SystemNative_Access", nullptr},
+		{0xb0df46ff09c57741, "AndroidCryptoNative_GetRsaParameters", nullptr},
+		{0xb0e18377ed603e0b, "SystemNative_GetGroupList", nullptr},
+		{0xb1c394b9992bd67d, "AndroidCryptoNative_EcDsaSign", nullptr},
+		{0xb1ff12f3bd735982, "AndroidCryptoNative_AeadCipherFinalEx", nullptr},
+		{0xb361006446f560e8, "SystemNative_LogError", nullptr},
+		{0xb3e1e5e50cde576e, "BrotliEncoderVersion", nullptr},
+		{0xb41fa43cc5c261cb, "BrotliDecoderGetErrorCode", nullptr},
+		{0xb4996dd1aba38200, "AndroidCryptoNative_EcDsaSize", nullptr},
+		{0xb516027cb59e6541, "BrotliDecoderIsFinished", nullptr},
+		{0xb575ec01a7a79f8f, "AndroidCryptoNative_DesCfb8", nullptr},
+		{0xb600c44028c1743d, "SystemNative_Socket", nullptr},
+		{0xb632e9bc6f7be0a9, "SystemNative_GetSockName", nullptr},
+		{0xb6540b73eff28747, "SystemNative_SetRawSockOpt", nullptr},
+		{0xb66be1550d27bfb4, "AndroidCryptoNative_GetECCurveParameters", nullptr},
+		{0xb69c3cc8b9f6a724, "BrotliDecoderIsUsed", nullptr},
+		{0xb6ab9abf7887911f, "SystemNative_ReadEvents", nullptr},
+		{0xb73c597de01bc0b2, "SystemNative_GetPwUidR", nullptr},
+		{0xb78af5975603cd20, "SystemNative_Sync", nullptr},
+		{0xb7bbbe2c16a565c6, "SystemNative_Calloc", nullptr},
+		{0xb81236cd1fe85cc9, "GlobalizationNative_GetLatestJapaneseEra", nullptr},
+		{0xb828d9e7df5437a5, "BrotliDecoderErrorString", nullptr},
+		{0xb95350c7ec77bc72, "GlobalizationNative_ChangeCase", nullptr},
+		{0xba2f6d298f3be8bc, "CryptoNative_EvpMd5", nullptr},
+		{0xbb3343826d504870, "SystemNative_GetBootTimeTicks", nullptr},
+		{0xbb5e970ecb6745da, "SystemNative_SymLink", nullptr},
+		{0xbbd20cce92ec2c12, "SystemNative_FcntlGetFD", nullptr},
+		{0xbcd9e53d2d288094, "SystemNative_GetNameInfo", nullptr},
+		{0xbd5a0be2f7904089, "AndroidCryptoNative_X509StoreAddCertificate", nullptr},
+		{0xbd89ef4df5486744, "SystemNative_Send", nullptr},
+		{0xbdbbd2898347c0d1, "AndroidCryptoNative_SSLStreamHandshake", nullptr},
+		{0xbdd3128e77381b01, "SystemNative_EnumerateInterfaceAddresses", nullptr},
+		{0xbe8df478de07c6d8, "BrotliDefaultFreeFunc", nullptr},
+		{0xc00ebc097b776c1f, "SystemNative_GetPriority", nullptr},
+		{0xc036b23d88fad91b, "SystemNative_iOSSupportVersion", nullptr},
+		{0xc0bb2dd0c5b74436, "CryptoNative_EnsureOpenSslInitialized", nullptr},
+		{0xc10e411c989a9314, "CompressionNative_Deflate", nullptr},
+		{0xc11cd661db8be230, "AndroidCryptoNative_Des3Cfb64", nullptr},
+		{0xc183a0550feea0d6, "BrotliEncoderCompressStream", nullptr},
+		{0xc19b94823ea1d39e, "CryptoNative_HmacOneShot", nullptr},
+		{0xc1b8a5f1c799e4bb, "BrotliGetDictionary", nullptr},
+		{0xc1c679eefc134d31, "SystemNative_LowLevelMonitor_Release", nullptr},
+		{0xc287daf58054a21d, "GlobalizationNative_EndsWith", nullptr},
+		{0xc2d5e1c465b2f5b6, "AndroidCryptoNative_DsaSizeP", nullptr},
+		{0xc3145e336c38379b, "AndroidCryptoNative_SSLStreamGetPeerCertificates", nullptr},
+		{0xc3c10021b10ba455, "SystemNative_GetEGid", nullptr},
+		{0xc3fe9394fe1f3f02, "SystemNative_GetSocketType", nullptr},
+		{0xc560d9947ab2a34d, "SystemNative_RegisterForSigChld", nullptr},
+		{0xc5bed971846027de, "SystemNative_GetCpuUtilization", nullptr},
+		{0xc69433678dd341ca, "SystemNative_ForkAndExecProcess", nullptr},
+		{0xc7815e0476511544, "AndroidCryptoNative_X509Encode", nullptr},
+		{0xc7ae1b8d93af5d73, "SystemNative_ChMod", nullptr},
+		{0xc7d536c0e7eb3fe2, "SystemNative_FreeSocketEventBuffer", nullptr},
+		{0xc7f81d5b58b65ac0, "BrotliEncoderCreateInstance", nullptr},
+		{0xc87a5ee4869035c6, "SystemNative_UninitializeConsoleAfterRead", nullptr},
+		{0xc8a52a8b6d96b32b, "AndroidCryptoNative_X509ExportPkcs7", nullptr},
+		{0xc8b772178f955d87, "GlobalizationNative_GetSortHandle", nullptr},
+		{0xc93df58ae5457bfd, "SystemNative_GetControlMessageBufferSize", nullptr},
+		{0xc956e528f995739c, "SystemNative_ReceiveMessage", nullptr},
+		{0xca001af79c0d7a8b, "CompressionNative_InflateEnd", nullptr},
+		{0xca48c3927c202794, "AndroidCryptoNative_GetECKeyParameters", nullptr},
+		{0xcaae6d345ba32c7b, "SystemNative_Kill", nullptr},
+		{0xcaec08aa13779f7f, "SystemNative_GetEnviron", nullptr},
+		{0xcaf599a20538b10b, "SystemNative_SetWindowSize", nullptr},
+		{0xcb4bcdafdc81d116, "AndroidCryptoNative_CipherCreatePartial", nullptr},
+		{0xcbbb90469d28cded, "SystemNative_SearchPath", nullptr},
+		{0xcc433093c073719e, "AndroidCryptoNative_SSLStreamRead", nullptr},
+		{0xcc43d880192dd6ff, "SystemNative_ConvertErrorPlatformToPal", nullptr},
+		{0xcc788c0474c3e178, "SystemNative_LSeek", nullptr},
+		{0xcd5d8a63493f5e38, "CompressionNative_InflateInit2_", nullptr},
+		{0xcdcb014df9a6eae2, "SystemNative_SetPort", nullptr},
+		{0xce36e2e1a139a020, "SystemNative_GetDefaultTimeZone", nullptr},
+		{0xce6ddfe40fed99d9, "SystemNative_PRead", nullptr},
+		{0xce9f8a6ac705faa5, "AndroidCryptoNative_X509DecodeCollection", nullptr},
+		{0xceba527295694651, "BrotliDecoderCreateInstance", nullptr},
+		{0xd0899515dfe85287, "GlobalizationNative_LoadICU", nullptr},
+		{0xd185dfe303ab91dd, "GlobalizationNative_CompareString", nullptr},
+		{0xd392d6ed5dcc111c, "SystemNative_GetDomainName", nullptr},
+		{0xd5264d57a926edfb, "GlobalizationNative_InitICUFunctions", nullptr},
+		{0xd55437b16dc84f3b, "SystemNative_GetIPv4GlobalStatistics", nullptr},
+		{0xd5c063a90ae882c1, "AndroidCryptoNative_CipherIsSupported", nullptr},
+		{0xd7d818c7640598dc, "AndroidCryptoNative_X509ChainGetCertificates", nullptr},
+		{0xd7f1a8f616897ace, "AndroidCryptoNative_Aes256Cfb8", nullptr},
+		{0xd88be8f9e9f28e90, "SystemNative_GetIcmpv4GlobalStatistics", nullptr},
+		{0xd8976692c4c68818, "SystemNative_GetEstimatedUdpListenerCount", nullptr},
+		{0xd8a9e47b6ca78448, "CryptoNative_ErrPeekLastError", nullptr},
+		{0xd995e71361e6ed2e, "GlobalizationNative_IsPredefinedLocale", nullptr},
+		{0xd9bd0b370726ce34, "AndroidCryptoNative_CipherReset", nullptr},
+		{0xda05c57c78aa6706, "SystemNative_LowLevelMonitor_Signal_Release", nullptr},
+		{0xda38bffa1d16cdd6, "SystemNative_SetLingerOption", nullptr},
+		{0xda4898a26933f73d, "AndroidCryptoNative_DsaVerify", nullptr},
+		{0xda6b3192974ca60e, "SystemNative_Open", nullptr},
+		{0xdab5eb45815daabc, "SystemNative_GetAtOutOfBandMark", nullptr},
+		{0xdae32aac0c0d305c, "SystemNative_ReadProcessStatusInfo", nullptr},
+		{0xdbb4752ed23670f0, "AndroidCryptoNative_DesCbc", nullptr},
+		{0xdbee22594fa8c585, "SystemNative_CreateAutoreleasePool", nullptr},
+		{0xdc51159ffe70b0e0, "BrotliDecoderVersion", nullptr},
+		{0xdc780005b0d39711, "AndroidCryptoNative_X509StoreGetPrivateKeyEntry", nullptr},
+		{0xdd4c03f06ce96e04, "AndroidCryptoNative_RsaDestroy", nullptr},
+		{0xdde06993f87d6ffc, "AndroidCryptoNative_Aes128Cfb128", nullptr},
+		{0xde1e22dd097f799c, "AndroidCryptoNative_CipherSetNonceLength", nullptr},
+		{0xde259001bf54e6f1, "AndroidCryptoNative_SSLStreamIsLocalCertificateUsed", nullptr},
+		{0xdec5c7544d2c8cb1, "AndroidCryptoNative_GetDsaParameters", nullptr},
+		{0xdf650444c8af0763, "SystemNative_FcntlGetIsNonBlocking", nullptr},
+		{0xdfede2defd776f7e, "AndroidCryptoNative_X509ChainSetCustomTrustStore", nullptr},
+		{0xe059239741e0011a, "AndroidCryptoNative_EcKeyCreateByKeyParameters", nullptr},
+		{0xe072da8f2d921f53, "GlobalizationNative_GetDefaultLocaleName", nullptr},
+		{0xe0a170d2b947a8fc, "SystemNative_SendMessage", nullptr},
+		{0xe0a601fd89d9b279, "SystemNative_SetErrNo", nullptr},
+		{0xe0f34ce89fd38aef, "AndroidCryptoNative_RsaPublicEncrypt", nullptr},
+		{0xe1930d112ce74c9e, "SystemNative_TryGetUInt32OSThreadId", nullptr},
+		{0xe20c29fb8b19da7b, "SystemNative_Listen", nullptr},
+		{0xe36a157177b2db08, "SystemNative_GetNonCryptographicallySecureRandomBytes", nullptr},
+		{0xe44f737a5bebdd90, "SystemNative_SetIPv4Address", nullptr},
+		{0xe582a4a60bb74c35, "SystemNative_GetProcAddress", nullptr},
+		{0xe604fca300068c0c, "AndroidCryptoNative_CipherCtxSetPadding", nullptr},
+		{0xe6838f2add787bfe, "SystemNative_FreeLibrary", nullptr},
+		{0xe73aeaf9e3a10343, "SystemNative_PWrite", nullptr},
+		{0xe78ff100d1d73d99, "SystemNative_SetReceiveTimeout", nullptr},
+		{0xe853ecfe4d402ed0, "SystemNative_Poll", nullptr},
+		{0xea21aa1f2b2a671c, "GlobalizationNative_LastIndexOf", nullptr},
+		{0xea5e6653389b924a, "CompressionNative_DeflateInit2_", nullptr},
+		{0xea61d6c040267b2d, "BrotliSetDictionaryData", nullptr},
+		{0xeaafb7963ceb9bf4, "SystemNative_GetTcpGlobalStatistics", nullptr},
+		{0xeab45239fb3f138d, "AndroidCryptoNative_GetBigNumBytes", nullptr},
+		{0xec67e4076662c2de, "SystemNative_GetDefaultSearchOrderPseudoHandle", nullptr},
+		{0xee4dd111dc8d98f3, "GlobalizationNative_GetJapaneseEraStartDate", nullptr},
+		{0xef71ee101b3ece96, "SystemNative_GetIcmpv6GlobalStatistics", nullptr},
+		{0xeff5d014640ae969, "AndroidCryptoNative_DeleteGlobalReference", nullptr},
+		{0xf0045895a9043221, "SystemNative_SearchPath_TempDirectory", nullptr},
+		{0xf0658a22dd5ede19, "SystemNative_SNPrintF_1I", nullptr},
+		{0xf0ec052da6c5fa70, "SystemNative_EnumerateGatewayAddressesForInterface", nullptr},
+		{0xf1577384f409ea85, "AndroidCryptoNative_BigNumToBinary", nullptr},
+		{0xf2c7fa39bf166188, "SystemNative_Free", nullptr},
+		{0xf2d074e0aeca51ce, "GlobalizationNative_GetLocales", nullptr},
+		{0xf3693f3cadb9b6f4, "GlobalizationNative_EnumCalendarInfo", nullptr},
+		{0xf38b47e43f352491, "SystemNative_GetUdpGlobalStatistics", nullptr},
+		{0xf432f105a045b088, "CryptoNative_ErrClearError", nullptr},
+		{0xf4dea312f71c5ff2, "AndroidCryptoNative_Aes128Ecb", nullptr},
+		{0xf4f5526ddc32beac, "CryptoNative_HmacDestroy", nullptr},
+		{0xf57f81262f07542c, "AndroidCryptoNative_Des3Ecb", nullptr},
+		{0xf63fa2bfce5c4f80, "GlobalizationNative_GetLocaleInfoGroupingSizes", nullptr},
+		{0xf6ede5d5d8729315, "SystemNative_WaitIdAnyExitedNoHangNoWait", nullptr},
+		{0xf75d4fdd6e749a84, "BrotliTransformDictionaryWord", nullptr},
+		{0xf7b334768844b502, "AndroidCryptoNative_X509StoreContainsCertificate", nullptr},
+		{0xf85b8ffeba9b06c1, "AndroidCryptoNative_X509StoreEnumerateTrustedCertificates", nullptr},
+		{0xf870179a8d8d1872, "SystemNative_PosixFAdvise", nullptr},
+		{0xf8c983dd21ef9fe6, "SystemNative_GetPid", nullptr},
+		{0xf96bc1e7e15e69f2, "AndroidCryptoNative_CipherUpdate", nullptr},
+		{0xf970881d4fa83e07, "AndroidCryptoNative_CipherCreate", nullptr},
+		{0xf9c3d216226b3355, "AndroidCryptoNative_CipherSetTagLength", nullptr},
+		{0xf9dea6e72f1fffc9, "CryptoNative_EvpMdCtxCreate", nullptr},
+		{0xfa21f0a127c9dce9, "GlobalizationNative_ChangeCaseInvariant", nullptr},
+		{0xfa2669c25616a8ff, "AndroidCryptoNative_X509IsKeyStorePrivateKeyEntry", nullptr},
+		{0xfa26b86cedf66721, "SystemNative_Sysctl", nullptr},
+		{0xfaa7766eaa2c54a5, "AndroidCryptoNative_DecodeRsaSubjectPublicKeyInfo", nullptr},
+		{0xfacf02f439426705, "GlobalizationNative_CloseSortHandle", nullptr},
+		{0xfb3e394cc613f202, "SystemNative_GetPeerName", nullptr},
+		{0xfbb57319454b1074, "SystemNative_GetSpaceInfoForMountPoint", nullptr},
+		{0xfc0bad2b1528000f, "AndroidCryptoNative_RsaVerificationPrimitive", nullptr},
+		{0xfcdeea476953780c, "AndroidCryptoNative_Aes192Cfb128", nullptr},
+		{0xfd2cdd99f11de76c, "AndroidCryptoNative_EcKeyGetSize", nullptr},
+		{0xfd4f2784ec1c98aa, "AndroidCryptoNative_SSLStreamRequestClientAuthentication", nullptr},
+		{0xfe3dd06281f7cd1f, "AndroidCryptoNative_SSLStreamInitialize", nullptr},
+		{0xff28b3bec4f32a2c, "SystemNative_GetFileSystemType", nullptr},
+		{0xff9b8d95b0e209fb, "BrotliEncoderHasMoreOutput", nullptr},
+		{0xffce9341c40b2b73, "BrotliDecoderDecompress", nullptr},
+	}};
+
+
+constexpr hash_t java_interop_library_hash = 0x54568ec36068e6b6;
+constexpr hash_t xa_internal_api_library_hash = 0x43fd1b21148361b2;
+constexpr hash_t android_liblog_library_hash = 0x1f2e4bce0544fb0a;
+constexpr hash_t system_native_library_hash = 0x4cd7bd0032e920e1;
+constexpr hash_t system_io_compression_native_library_hash = 0x9190f4cb761b1d3c;
+constexpr hash_t system_security_cryptography_native_android_library_hash = 0x1848c0093f0afd8;
+constexpr hash_t system_globalization_native_library_hash = 0x28b5c8fca080abd5;
+#else
+	//32-bit internal p/invoke table
+	std::array<PinvokeEntry, 10> internal_pinvokes {{
+		{0xb7a486a, "monodroid_TypeManager_get_java_class_name", reinterpret_cast<void*>(&monodroid_TypeManager_get_java_class_name)},
+		{0x39e5b5d4, "__android_log_print", reinterpret_cast<void*>(&__android_log_print)},
+		{0x656e00bd, "clr_typemap_managed_to_java", reinterpret_cast<void*>(&clr_typemap_managed_to_java)},
+		{0xa04e5d1c, "monodroid_free", reinterpret_cast<void*>(&monodroid_free)},
+		{0xb02468aa, "_monodroid_gref_get", reinterpret_cast<void*>(&_monodroid_gref_get)},
+		{0xb6431f9a, "clr_typemap_java_to_managed", reinterpret_cast<void*>(&clr_typemap_java_to_managed)},
+		{0xbe8d7701, "_monodroid_gref_log_new", reinterpret_cast<void*>(&_monodroid_gref_log_new)},
+		{0xc5146c54, "_monodroid_gref_log_delete", reinterpret_cast<void*>(&_monodroid_gref_log_delete)},
+		{0xe7e77ca5, "_monodroid_gref_log", reinterpret_cast<void*>(&_monodroid_gref_log)},
+		{0xfa4e32ca, "monodroid_log", reinterpret_cast<void*>(&monodroid_log)},
+	}};
+
+	//32-bit DotNet p/invoke table
+	std::array<PinvokeEntry, 477> dotnet_pinvokes {{
+		{0xaf6b1c, "AndroidCryptoNative_RsaPrivateDecrypt", nullptr},
+		{0x1733089, "SystemNative_SetTerminalInvalidationHandler", nullptr},
+		{0x1dd1f00, "AndroidCryptoNative_Aes192Cfb8", nullptr},
+		{0x23a0578, "AndroidCryptoNative_NewGlobalReference", nullptr},
+		{0x2f05496, "SystemNative_GetPeerName", nullptr},
+		{0x3295077, "SystemNative_MapTcpState", nullptr},
+		{0x3d9bc5f, "SystemNative_Unlink", nullptr},
+		{0x3e12cb4, "SystemNative_INotifyInit", nullptr},
+		{0x5b0fb1d, "SystemNative_InitializeConsoleBeforeRead", nullptr},
+		{0x80f30b4, "BrotliDecoderTakeOutput", nullptr},
+		{0x84ccf89, "SystemNative_MSync", nullptr},
+		{0x8c636a2, "SystemNative_FcntlSetPipeSz", nullptr},
+		{0x8de5b3d, "SystemNative_GetSid", nullptr},
+		{0x92bf2d9, "AndroidCryptoNative_EcKeyGetSize", nullptr},
+		{0xaa46d20, "SystemNative_SNPrintF", nullptr},
+		{0xaa7c86e, "SystemNative_Exit", nullptr},
+		{0xb6a80bd, "SystemNative_SetAddressFamily", nullptr},
+		{0xbdd984d, "SystemNative_SetWindowSize", nullptr},
+		{0xcc59904, "CryptoNative_HmacDestroy", nullptr},
+		{0xd5ca844, "SystemNative_CreateSocketEventPort", nullptr},
+		{0xd98d741, "SystemNative_Kill", nullptr},
+		{0xdfe3e26, "SystemNative_Connectx", nullptr},
+		{0xfc48476, "SystemNative_GetNonCryptographicallySecureRandomBytes", nullptr},
+		{0x10d108c9, "SystemNative_FreeHostEntry", nullptr},
+		{0x1165644f, "SystemNative_GetOSArchitecture", nullptr},
+		{0x11778651, "SystemNative_ConfigureTerminalForChildProcess", nullptr},
+		{0x1178ebdd, "CryptoNative_EvpDigestFinalEx", nullptr},
+		{0x11a2796d, "SystemNative_GetTcpGlobalStatistics", nullptr},
+		{0x11d9981e, "SystemNative_MProtect", nullptr},
+		{0x12105897, "GlobalizationNative_NormalizeString", nullptr},
+		{0x12b01cc9, "GlobalizationNative_IsNormalized", nullptr},
+		{0x12fdf5c3, "SystemNative_ConvertErrorPlatformToPal", nullptr},
+		{0x1348bf25, "AndroidCryptoNative_SSLStreamWrite", nullptr},
+		{0x1376985b, "SystemNative_SetSockOpt", nullptr},
+		{0x13925de2, "SystemNative_GetLingerOption", nullptr},
+		{0x13f565a9, "SystemNative_GetControlMessageBufferSize", nullptr},
+		{0x142a08a1, "SystemNative_PosixFAdvise", nullptr},
+		{0x16d98313, "GlobalizationNative_IndexOf", nullptr},
+		{0x17549123, "SystemNative_Connect", nullptr},
+		{0x17a5d095, "AndroidCryptoNative_SSLStreamGetApplicationProtocol", nullptr},
+		{0x17b96c39, "SystemNative_FreeSocketEventBuffer", nullptr},
+		{0x1904820d, "SystemNative_GetHostEntryForName", nullptr},
+		{0x19b6a696, "AndroidCryptoNative_X509DecodeCollection", nullptr},
+		{0x1a302b28, "SystemNative_SchedSetAffinity", nullptr},
+		{0x1aa4105d, "GlobalizationNative_GetSortKey", nullptr},
+		{0x1ab1248e, "SystemNative_GetHostName", nullptr},
+		{0x1bf277c4, "SystemNative_WaitForSocketEvents", nullptr},
+		{0x1c4778bf, "SystemNative_AlignedFree", nullptr},
+		{0x1cb466df, "AndroidCryptoNative_RsaGenerateKeyEx", nullptr},
+		{0x1cf7b52c, "SystemNative_MAdvise", nullptr},
+		{0x1eb6eaaa, "CryptoNative_GetRandomBytes", nullptr},
+		{0x1ebc63c1, "AndroidCryptoNative_X509ChainDestroyContext", nullptr},
+		{0x1f186646, "AndroidCryptoNative_CipherSetKeyAndIV", nullptr},
+		{0x1f1cd573, "AndroidCryptoNative_Des3Cfb64", nullptr},
+		{0x1f998744, "AndroidCryptoNative_Aes128Cfb128", nullptr},
+		{0x1fdcd1e0, "CryptoNative_ErrPeekError", nullptr},
+		{0x212e38c4, "SystemNative_GetUdpGlobalStatistics", nullptr},
+		{0x218fa94a, "AndroidCryptoNative_X509StoreDeleteEntry", nullptr},
+		{0x22011e2b, "SystemNative_SetLingerOption", nullptr},
+		{0x224ebd71, "SystemNative_Listen", nullptr},
+		{0x2253b591, "BrotliGetTransforms", nullptr},
+		{0x226eec4d, "SystemNative_Abort", nullptr},
+		{0x229f73d4, "AndroidCryptoNative_RsaUpRef", nullptr},
+		{0x22bbb587, "AndroidCryptoNative_SSLStreamGetPeerCertificate", nullptr},
+		{0x2304e65b, "SystemNative_SetRLimit", nullptr},
+		{0x23cfcfb0, "BrotliTransformDictionaryWord", nullptr},
+		{0x260a3e8d, "CompressionNative_DeflateInit2_", nullptr},
+		{0x289b5430, "SystemNative_Log", nullptr},
+		{0x28d95a99, "SystemNative_CanGetHiddenFlag", nullptr},
+		{0x28f3db4b, "SystemNative_ShmUnlink", nullptr},
+		{0x2af6aa40, "SystemNative_Access", nullptr},
+		{0x2b117055, "BrotliDecoderDecompress", nullptr},
+		{0x2b7293c5, "SystemNative_GetTimestamp", nullptr},
+		{0x2b747a9c, "SystemNative_MkNod", nullptr},
+		{0x2bc9ff5e, "AndroidCryptoNative_SSLStreamGetProtocol", nullptr},
+		{0x2c4415fd, "AndroidCryptoNative_AeadCipherFinalEx", nullptr},
+		{0x2c467430, "AndroidCryptoNative_GetECCurveParameters", nullptr},
+		{0x2d21ad97, "SystemNative_GetReadDirRBufferSize", nullptr},
+		{0x2d6e4a1c, "AndroidCryptoNative_X509StoreRemoveCertificate", nullptr},
+		{0x2e66f31b, "BrotliDecoderDestroyInstance", nullptr},
+		{0x2eb28fb6, "SystemNative_GetIPv4Address", nullptr},
+		{0x2f7d80dd, "GlobalizationNative_WindowsIdToIanaId", nullptr},
+		{0x2ff73621, "CryptoNative_ErrReasonErrorString", nullptr},
+		{0x30af09b7, "AndroidCryptoNative_DecodeRsaSubjectPublicKeyInfo", nullptr},
+		{0x31120969, "SystemNative_Malloc", nullptr},
+		{0x3374b950, "SystemNative_GetLoadLibraryError", nullptr},
+		{0x34867c2f, "SystemNative_TryGetUInt32OSThreadId", nullptr},
+		{0x349c5a8f, "SystemNative_GetNetworkInterfaces", nullptr},
+		{0x354aa58f, "AndroidCryptoNative_DsaKeyCreateByExplicitParameters", nullptr},
+		{0x363c0010, "CryptoNative_EvpDigestUpdate", nullptr},
+		{0x367eee31, "AndroidCryptoNative_SSLStreamIsLocalCertificateUsed", nullptr},
+		{0x38406fa3, "GlobalizationNative_ChangeCaseInvariant", nullptr},
+		{0x38575bc5, "SystemNative_GetUnixRelease", nullptr},
+		{0x388a31d4, "SystemNative_PathConf", nullptr},
+		{0x3a238b9f, "AndroidCryptoNative_RsaVerificationPrimitive", nullptr},
+		{0x3a861d34, "SystemNative_GetNativeIPInterfaceStatistics", nullptr},
+		{0x3af56a10, "AndroidCryptoNative_RsaSize", nullptr},
+		{0x3b286185, "GlobalizationNative_ChangeCaseTurkish", nullptr},
+		{0x3bf3d465, "SystemNative_GetIPv6MulticastOption", nullptr},
+		{0x3cb49aae, "SystemNative_GetPwNamR", nullptr},
+		{0x3d150bdf, "AndroidCryptoNative_Aes128Ecb", nullptr},
+		{0x3d823979, "GlobalizationNative_ToUnicode", nullptr},
+		{0x3da52690, "SystemNative_FcntlCanGetSetPipeSz", nullptr},
+		{0x3de52faf, "AndroidCryptoNative_CipherUpdateAAD", nullptr},
+		{0x3df8d649, "SystemNative_SetDelayedSigChildConsoleConfigurationHandler", nullptr},
+		{0x3e175e7c, "AndroidCryptoNative_Aes256Cfb128", nullptr},
+		{0x3e273961, "SystemNative_StrErrorR", nullptr},
+		{0x3e48f022, "SystemNative_GetMaximumAddressSize", nullptr},
+		{0x3e778b38, "BrotliDecoderVersion", nullptr},
+		{0x3ea31c40, "SystemNative_GetAddressFamily", nullptr},
+		{0x3efdb5a0, "SystemNative_SendMessage", nullptr},
+		{0x3f47618f, "CryptoNative_EnsureOpenSslInitialized", nullptr},
+		{0x3f793993, "SystemNative_LowLevelMonitor_Signal_Release", nullptr},
+		{0x40b0026c, "CompressionNative_DeflateEnd", nullptr},
+		{0x40e64bdd, "CryptoNative_ErrClearError", nullptr},
+		{0x413b9801, "SystemNative_Read", nullptr},
+		{0x41818c1d, "SystemNative_GetPriority", nullptr},
+		{0x41cf0c16, "AndroidCryptoNative_CipherCreate", nullptr},
+		{0x42955366, "SystemNative_Disconnect", nullptr},
+		{0x42afcfbb, "AndroidCryptoNative_CipherCreatePartial", nullptr},
+		{0x430352b3, "SystemNative_GetNameInfo", nullptr},
+		{0x43f6cea1, "AndroidCryptoNative_DesCfb8", nullptr},
+		{0x4543d533, "AndroidCryptoNative_EcDsaVerify", nullptr},
+		{0x45a00971, "GlobalizationNative_CloseSortHandle", nullptr},
+		{0x45f09dca, "AndroidCryptoNative_CipherSetTagLength", nullptr},
+		{0x46268e76, "GlobalizationNative_GetCalendarInfo", nullptr},
+		{0x477f60cf, "SystemNative_OpenDir", nullptr},
+		{0x47a82b4e, "SystemNative_AlignedRealloc", nullptr},
+		{0x48c17c9b, "SystemNative_Sysctl", nullptr},
+		{0x493888ee, "CompressionNative_Crc32", nullptr},
+		{0x494ef6d4, "SystemNative_GetIPv4MulticastOption", nullptr},
+		{0x496f1885, "SystemNative_GetSocketErrorOption", nullptr},
+		{0x49c2af32, "SystemNative_GetBootTimeTicks", nullptr},
+		{0x49c81782, "SystemNative_MkDir", nullptr},
+		{0x49f60a0f, "GlobalizationNative_GetLocales", nullptr},
+		{0x4a4ef46f, "SystemNative_FcntlGetFD", nullptr},
+		{0x4a98a396, "GlobalizationNative_GetLocaleInfoInt", nullptr},
+		{0x4b78d330, "CryptoNative_HmacCurrent", nullptr},
+		{0x4c2eae6c, "GlobalizationNative_EnumCalendarInfo", nullptr},
+		{0x4c6d50ba, "SystemNative_GetIPv4GlobalStatistics", nullptr},
+		{0x4ca38207, "AndroidCryptoNative_X509StoreEnumerateTrustedCertificates", nullptr},
+		{0x4cb997ae, "BrotliEncoderCompress", nullptr},
+		{0x4d1a35d1, "SystemNative_LowLevelMonitor_Release", nullptr},
+		{0x4d75bb15, "SystemNative_WaitIdAnyExitedNoHangNoWait", nullptr},
+		{0x4dbf0c74, "SystemNative_CreateSocketEventBuffer", nullptr},
+		{0x4e4d4f2a, "SystemNative_SetIPv6Address", nullptr},
+		{0x4f6011da, "SystemNative_GetPort", nullptr},
+		{0x4f6c3726, "SystemNative_FcntlGetIsNonBlocking", nullptr},
+		{0x50e88639, "CryptoNative_HmacUpdate", nullptr},
+		{0x514e739b, "AndroidCryptoNative_EcKeyCreateByExplicitParameters", nullptr},
+		{0x52590509, "AndroidCryptoNative_Aes128Cbc", nullptr},
+		{0x526c9f90, "SystemNative_GetNumRoutes", nullptr},
+		{0x52896a81, "SystemNative_ChMod", nullptr},
+		{0x538521c9, "GlobalizationNative_IanaIdToWindowsId", nullptr},
+		{0x54d6c29d, "GlobalizationNative_GetJapaneseEraStartDate", nullptr},
+		{0x5600bd0d, "AndroidCryptoNative_SSLStreamCreate", nullptr},
+		{0x561fb6ff, "SystemNative_FStat", nullptr},
+		{0x564f6794, "AndroidCryptoNative_Pbkdf2", nullptr},
+		{0x56993aa9, "SystemNative_SetKeypadXmit", nullptr},
+		{0x57bdcc46, "SystemNative_Open", nullptr},
+		{0x581adfc6, "SystemNative_GetSignalForBreak", nullptr},
+		{0x5906e1ba, "SystemNative_Close", nullptr},
+		{0x591c5746, "AndroidCryptoNative_EcKeyUpRef", nullptr},
+		{0x59840533, "AndroidCryptoNative_X509Decode", nullptr},
+		{0x5989ad17, "SystemNative_GetIcmpv4GlobalStatistics", nullptr},
+		{0x599921d3, "SystemNative_SysConf", nullptr},
+		{0x59b67f4d, "AndroidCryptoNative_SSLGetSupportedProtocols", nullptr},
+		{0x59e712d5, "SystemNative_MkdTemp", nullptr},
+		{0x5a492732, "SystemNative_FcntlSetFD", nullptr},
+		{0x5ccc38dd, "AndroidCryptoNative_SSLStreamGetPeerCertificates", nullptr},
+		{0x5e9ef1a2, "SystemNative_GetAtOutOfBandMark", nullptr},
+		{0x5eb4f827, "SystemNative_LockFileRegion", nullptr},
+		{0x5ed67634, "SystemNative_GetPwUidR", nullptr},
+		{0x5efc6409, "SystemNative_ReceiveSocketError", nullptr},
+		{0x5f706f52, "AndroidCryptoNative_RsaSignPrimitive", nullptr},
+		{0x5fc58bed, "SystemNative_GetEstimatedTcpConnectionCount", nullptr},
+		{0x60571eb9, "AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration", nullptr},
+		{0x6068baa0, "AndroidCryptoNative_GetRsaParameters", nullptr},
+		{0x608ee1a5, "SystemNative_Calloc", nullptr},
+		{0x60c353e5, "SystemNative_SetPosixSignalHandler", nullptr},
+		{0x613c0080, "AndroidCryptoNative_Aes192Ccm", nullptr},
+		{0x626db703, "SystemNative_LStat", nullptr},
+		{0x6288dd9a, "SystemNative_SetSignalForBreak", nullptr},
+		{0x62a36e75, "AndroidCryptoNative_X509ChainGetErrorCount", nullptr},
+		{0x639b2b1d, "AndroidCryptoNative_DsaVerify", nullptr},
+		{0x6436999d, "AndroidCryptoNative_SSLStreamRead", nullptr},
+		{0x6441bc65, "CryptoNative_EvpSha256", nullptr},
+		{0x64f12e5b, "BrotliDecoderIsFinished", nullptr},
+		{0x661c5218, "SystemNative_GetDomainName", nullptr},
+		{0x6661a841, "BrotliDecoderDecompressStream", nullptr},
+		{0x66b5bf9d, "GlobalizationNative_IsPredefinedLocale", nullptr},
+		{0x674bdf7f, "SystemNative_DisablePosixSignalHandling", nullptr},
+		{0x679dd832, "SystemNative_SetPort", nullptr},
+		{0x679f9b4e, "SystemNative_FcntlGetPipeSz", nullptr},
+		{0x67de0842, "SystemNative_Dup", nullptr},
+		{0x687726ff, "AndroidCryptoNative_EcKeyGetCurveName", nullptr},
+		{0x68bdc398, "SystemNative_INotifyAddWatch", nullptr},
+		{0x68c949a0, "AndroidCryptoNative_X509GetContentType", nullptr},
+		{0x68f9f52f, "AndroidCryptoNative_CipherIsSupported", nullptr},
+		{0x6907c8eb, "BrotliEncoderSetParameter", nullptr},
+		{0x6adb646e, "SystemNative_ReadDirR", nullptr},
+		{0x6b5343a0, "SystemNative_SetErrNo", nullptr},
+		{0x6bbd3d10, "SystemNative_GetRLimit", nullptr},
+		{0x6be1e33d, "SystemNative_EnumerateInterfaceAddresses", nullptr},
+		{0x6cda2cf8, "SystemNative_SetSendTimeout", nullptr},
+		{0x6d48392a, "SystemNative_Stat", nullptr},
+		{0x6ece5fe6, "SystemNative_GetPid", nullptr},
+		{0x6ef4e421, "AndroidCryptoNative_CipherDestroy", nullptr},
+		{0x6f18d737, "GlobalizationNative_InitICUFunctions", nullptr},
+		{0x6f695cb8, "SystemNative_RmDir", nullptr},
+		{0x6fa886b1, "SystemNative_GetSockName", nullptr},
+		{0x6fc36e5f, "GlobalizationNative_StartsWith", nullptr},
+		{0x708e7911, "SystemNative_SetIPv4Address", nullptr},
+		{0x70d4f7e6, "SystemNative_GetActiveTcpConnectionInfos", nullptr},
+		{0x70e91ddd, "SystemNative_FChMod", nullptr},
+		{0x71698a7f, "SystemNative_GetDomainSocketSizes", nullptr},
+		{0x7243c4b4, "AndroidCryptoNative_Des3Cfb8", nullptr},
+		{0x758dd6aa, "SystemNative_GetTimeZoneData", nullptr},
+		{0x759f5b1e, "AndroidCryptoNative_Aes256Cfb8", nullptr},
+		{0x75b11f61, "BrotliDecoderGetErrorCode", nullptr},
+		{0x76e97b2e, "SystemNative_Rename", nullptr},
+		{0x78c1eb52, "AndroidCryptoNative_Des3Ecb", nullptr},
+		{0x7a0529c1, "SystemNative_InitializeTerminalAndSignalHandling", nullptr},
+		{0x7a4012d2, "GlobalizationNative_GetICUVersion", nullptr},
+		{0x7aa30494, "Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate", nullptr},
+		{0x7ad3b820, "AndroidCryptoNative_Aes192Cfb128", nullptr},
+		{0x7cb19137, "SystemNative_GetIcmpv6GlobalStatistics", nullptr},
+		{0x7d0c477d, "CryptoNative_ErrPeekLastError", nullptr},
+		{0x7d2bb98a, "SystemNative_MksTemps", nullptr},
+		{0x7de70253, "SystemNative_ConvertErrorPalToPlatform", nullptr},
+		{0x7e882ae5, "BrotliEncoderIsFinished", nullptr},
+		{0x7e9a677b, "GlobalizationNative_GetSortVersion", nullptr},
+		{0x7f5d9e25, "SystemNative_MUnmap", nullptr},
+		{0x80d5027e, "CryptoNative_EvpMdCtxCopyEx", nullptr},
+		{0x80deced4, "SystemNative_CreateAutoreleasePool", nullptr},
+		{0x81a5efac, "SystemNative_SetEUid", nullptr},
+		{0x82484cbf, "CryptoNative_EvpDigestOneShot", nullptr},
+		{0x8289a6f7, "BrotliDefaultFreeFunc", nullptr},
+		{0x83dad9bf, "SystemNative_Pipe", nullptr},
+		{0x83db1b72, "CryptoNative_EvpDigestCurrent", nullptr},
+		{0x84662605, "CompressionNative_Deflate", nullptr},
+		{0x8526c9e8, "SystemNative_SetRawSockOpt", nullptr},
+		{0x8574b133, "AndroidCryptoNative_Aes192Ecb", nullptr},
+		{0x85abed93, "GlobalizationNative_GetCalendars", nullptr},
+		{0x8808879d, "AndroidCryptoNative_X509StoreGetPrivateKeyEntry", nullptr},
+		{0x88a7558d, "SystemNative_GetDefaultSearchOrderPseudoHandle", nullptr},
+		{0x8ba80ef4, "GlobalizationNative_InitOrdinalCasingPage", nullptr},
+		{0x8bdaf06c, "SystemNative_GetWindowSize", nullptr},
+		{0x8bfcd7ba, "CompressionNative_Inflate", nullptr},
+		{0x8d38b733, "SystemNative_LSeek", nullptr},
+		{0x8f4e59f1, "SystemNative_ReadStdin", nullptr},
+		{0x8f628a8d, "GlobalizationNative_CompareString", nullptr},
+		{0x909e12ee, "SystemNative_SearchPath", nullptr},
+		{0x910b7740, "SystemNative_GetFormatInfoForMountPoint", nullptr},
+		{0x913a3d68, "SystemNative_GetDeviceIdentifiers", nullptr},
+		{0x9216d936, "SystemNative_GetGroupName", nullptr},
+		{0x94477030, "AndroidCryptoNative_Aes256Ccm", nullptr},
+		{0x95e99740, "AndroidCryptoNative_SSLStreamInitialize", nullptr},
+		{0x960d4fc0, "SystemNative_GetDefaultTimeZone", nullptr},
+		{0x966f54af, "CryptoNative_EvpSha384", nullptr},
+		{0x96912459, "AndroidCryptoNative_X509GetCertificateForPrivateKeyEntry", nullptr},
+		{0x9787b4b4, "CryptoNative_EvpMdSize", nullptr},
+		{0x98105435, "SystemNative_GetIPv6Address", nullptr},
+		{0x984edaf1, "AndroidCryptoNative_RsaDestroy", nullptr},
+		{0x9852b0fa, "SystemNative_INotifyRemoveWatch", nullptr},
+		{0x98954db8, "AndroidCryptoNative_X509StoreOpenDefault", nullptr},
+		{0x98ca7f1c, "SystemNative_ChDir", nullptr},
+		{0x990163b4, "SystemNative_Receive", nullptr},
+		{0x996952b3, "AndroidCryptoNative_CipherFinalEx", nullptr},
+		{0x9a005080, "SystemNative_Bind", nullptr},
+		{0x9a84ffd3, "AndroidCryptoNative_ChaCha20Poly1305", nullptr},
+		{0x9abfce84, "SystemNative_GetEnviron", nullptr},
+		{0x9bda7eb1, "SystemNative_ReadProcessStatusInfo", nullptr},
+		{0x9cd6cae8, "AndroidCryptoNative_SSLStreamRelease", nullptr},
+		{0x9d102d58, "CompressionNative_InflateEnd", nullptr},
+		{0x9d2f90cf, "GlobalizationNative_GetLatestJapaneseEra", nullptr},
+		{0x9d7f4af6, "SystemNative_ReceiveMessage", nullptr},
+		{0x9dc3baed, "SystemNative_LowLevelMonitor_Destroy", nullptr},
+		{0x9e366e9c, "SystemNative_InterfaceNameToIndex", nullptr},
+		{0x9e717f20, "SystemNative_LChflagsCanSetHiddenFlag", nullptr},
+		{0x9f47b32d, "SystemNative_ShmOpen", nullptr},
+		{0x9feb81cb, "SystemNative_SNPrintF_1I", nullptr},
+		{0xa0db1858, "SystemNative_GetEnv", nullptr},
+		{0xa1295a9f, "SystemNative_MkFifo", nullptr},
+		{0xa1bec9da, "SystemNative_LogError", nullptr},
+		{0xa1d774fc, "BrotliEncoderHasMoreOutput", nullptr},
+		{0xa2430b33, "SystemNative_SearchPath_TempDirectory", nullptr},
+		{0xa25daa0e, "BrotliSetDictionaryData", nullptr},
+		{0xa2d2f390, "SystemNative_TryChangeSocketEventRegistration", nullptr},
+		{0xa39be756, "GlobalizationNative_LoadICU", nullptr},
+		{0xa4636764, "AndroidCryptoNative_X509ChainCreateContext", nullptr},
+		{0xa477c74e, "SystemNative_TryGetIPPacketInformation", nullptr},
+		{0xa635da0f, "AndroidCryptoNative_SSLStreamShutdown", nullptr},
+		{0xa691d151, "AndroidCryptoNative_Aes192Cbc", nullptr},
+		{0xa72ce322, "AndroidCryptoNative_DeleteGlobalReference", nullptr},
+		{0xa8074d4c, "GlobalizationNative_GetSortHandle", nullptr},
+		{0xa826eabe, "SystemNative_FcntlSetIsNonBlocking", nullptr},
+		{0xa829138a, "SystemNative_GetProcessPath", nullptr},
+		{0xa8701bcf, "AndroidCryptoNative_SSLStreamRequestClientAuthentication", nullptr},
+		{0xa8da7ba1, "BrotliEncoderVersion", nullptr},
+		{0xa936bc40, "AndroidCryptoNative_EcDsaSize", nullptr},
+		{0xa9c29be5, "SystemNative_SetIPv6MulticastOption", nullptr},
+		{0xa9c84a4a, "AndroidCryptoNative_X509ExportPkcs7", nullptr},
+		{0xaa13ec2b, "GlobalizationNative_GetLocaleInfoGroupingSizes", nullptr},
+		{0xaa2f32ad, "SystemNative_FTruncate", nullptr},
+		{0xab37a684, "CryptoNative_HmacCreate", nullptr},
+		{0xab3d1641, "AndroidCryptoNative_DesEcb", nullptr},
+		{0xabe6739f, "BrotliGetDictionary", nullptr},
+		{0xacc26fa4, "AndroidCryptoNative_SSLStreamCreateWithCertificates", nullptr},
+		{0xacc28460, "SystemNative_GetProcAddress", nullptr},
+		{0xad7fbde5, "SystemNative_FUTimens", nullptr},
+		{0xae443204, "SystemNative_GetSockOpt", nullptr},
+		{0xae449ad1, "BrotliDecoderIsUsed", nullptr},
+		{0xae8752e4, "GlobalizationNative_ToAscii", nullptr},
+		{0xafb02e71, "BrotliEncoderMaxCompressedSize", nullptr},
+		{0xb01e9c27, "AndroidCryptoNative_SSLStreamSetEnabledProtocols", nullptr},
+		{0xb030a893, "SystemNative_LowLevelMonitor_Acquire", nullptr},
+		{0xb0e270a0, "BrotliEncoderDestroyInstance", nullptr},
+		{0xb22a12be, "BrotliEncoderCreateInstance", nullptr},
+		{0xb26f05b6, "SystemNative_PWrite", nullptr},
+		{0xb2965ccd, "CryptoNative_GetMaxMdSize", nullptr},
+		{0xb2985645, "AndroidCryptoNative_X509StoreEnumerateCertificates", nullptr},
+		{0xb4110b14, "AndroidCryptoNative_DsaGenerateKey", nullptr},
+		{0xb427959c, "SystemNative_FLock", nullptr},
+		{0xb439ebdb, "AndroidCryptoNative_RegisterRemoteCertificateValidationCallback", nullptr},
+		{0xb444f04a, "SystemNative_Accept", nullptr},
+		{0xb448a24a, "SystemNative_SymLink", nullptr},
+		{0xb4e5c37d, "SystemNative_UTimensat", nullptr},
+		{0xb584e8fb, "GlobalizationNative_GetTimeZoneDisplayName", nullptr},
+		{0xb5a5754a, "SystemNative_UninitializeConsoleAfterRead", nullptr},
+		{0xb5db6a51, "SystemNative_PWriteV", nullptr},
+		{0xb628f475, "SystemNative_GetBytesAvailable", nullptr},
+		{0xb7041ffa, "SystemNative_GetControlCharacters", nullptr},
+		{0xb7cc3cd1, "AndroidCryptoNative_DesCbc", nullptr},
+		{0xb7ebdf2c, "AndroidCryptoNative_X509IsKeyStorePrivateKeyEntry", nullptr},
+		{0xb80f233c, "SystemNative_FSync", nullptr},
+		{0xb84914f1, "SystemNative_EnumerateGatewayAddressesForInterface", nullptr},
+		{0xb862b34e, "AndroidCryptoNative_CipherSetNonceLength", nullptr},
+		{0xb884b933, "SystemNative_StdinReady", nullptr},
+		{0xb96c2133, "SystemNative_GetErrNo", nullptr},
+		{0xb97add7d, "SystemNative_CreateNetworkChangeListenerSocket", nullptr},
+		{0xb9e6cb2c, "SystemNative_RealPath", nullptr},
+		{0xba284ef4, "CryptoNative_EvpSha1", nullptr},
+		{0xbb06f5e1, "AndroidCryptoNative_Aes192Gcm", nullptr},
+		{0xbb25ff40, "AndroidCryptoNative_SSLStreamSetTargetHost", nullptr},
+		{0xbb2ca4f3, "SystemNative_Link", nullptr},
+		{0xbb92466f, "SystemNative_AlignedAlloc", nullptr},
+		{0xbd658356, "CryptoNative_ErrErrorStringN", nullptr},
+		{0xbdbf2140, "SystemNative_SchedGetAffinity", nullptr},
+		{0xbec8a3f2, "SystemNative_FChflags", nullptr},
+		{0xbf4eeb78, "AndroidCryptoNative_GetBigNumBytes", nullptr},
+		{0xbf9766c3, "AndroidCryptoNative_SSLStreamHandshake", nullptr},
+		{0xbfa0ce53, "SystemNative_GetPlatformSignalNumber", nullptr},
+		{0xbfaad12d, "BrotliDecoderSetParameter", nullptr},
+		{0xc090b1d3, "CryptoNative_EvpSha512", nullptr},
+		{0xc0d66913, "SystemNative_GetUnixVersion", nullptr},
+		{0xc11dec94, "SystemNative_FAllocate", nullptr},
+		{0xc1243135, "AndroidCryptoNative_Aes128Gcm", nullptr},
+		{0xc1e4e6f6, "AndroidCryptoNative_BigNumToBinary", nullptr},
+		{0xc25ffc33, "BrotliEncoderCompressStream", nullptr},
+		{0xc3812682, "AndroidCryptoNative_X509ChainGetErrors", nullptr},
+		{0xc3dcc3a0, "AndroidCryptoNative_SetRsaParameters", nullptr},
+		{0xc3e6ff56, "SystemNative_LowLevelMonitor_Create", nullptr},
+		{0xc475f41c, "AndroidCryptoNative_X509StoreContainsCertificate", nullptr},
+		{0xc4ac1723, "AndroidCryptoNative_Aes256Cbc", nullptr},
+		{0xc55548f2, "SystemNative_HandleNonCanceledPosixSignal", nullptr},
+		{0xc57b40fa, "SystemNative_LoadLibrary", nullptr},
+		{0xc5a83c28, "AndroidCryptoNative_SSLStreamCreateWithKeyStorePrivateKeyEntry", nullptr},
+		{0xc6d5929c, "SystemNative_LowLevelMonitor_TimedWait", nullptr},
+		{0xc6f2fb9e, "AndroidCryptoNative_X509ChainSetCustomTrustStore", nullptr},
+		{0xc717b16e, "CryptoNative_EvpMdCtxDestroy", nullptr},
+		{0xc746b70c, "AndroidCryptoNative_DsaSign", nullptr},
+		{0xc83527e0, "CryptoNative_HmacReset", nullptr},
+		{0xc89ccd22, "SystemNative_SchedGetCpu", nullptr},
+		{0xc8cce896, "SystemNative_GetSocketType", nullptr},
+		{0xc8e06b20, "AndroidCryptoNative_X509ChainGetCertificates", nullptr},
+		{0xc9b017c8, "AndroidCryptoNative_EcKeyCreateByOid", nullptr},
+		{0xca4dad90, "GlobalizationNative_ChangeCase", nullptr},
+		{0xca5aab33, "SystemNative_Sync", nullptr},
+		{0xcb458400, "CryptoNative_ErrGetErrorAlloc", nullptr},
+		{0xcb746e5c, "SystemNative_SetIPv4MulticastOption", nullptr},
+		{0xcb85cd8e, "AndroidCryptoNative_EcdhDeriveKey", nullptr},
+		{0xccc0dd15, "SystemNative_RegisterForSigChld", nullptr},
+		{0xcdfb627d, "SystemNative_FreeLibrary", nullptr},
+		{0xce91e293, "SystemNative_GetGroupList", nullptr},
+		{0xcf0912c8, "GlobalizationNative_GetLocaleTimeFormat", nullptr},
+		{0xcf9bcc75, "AndroidCryptoNative_Aes256Gcm", nullptr},
+		{0xcfa9e6f1, "AndroidCryptoNative_GetDsaParameters", nullptr},
+		{0xcff9b341, "SystemNative_GetSystemTimeAsTicks", nullptr},
+		{0xd199e841, "SystemNative_GetEstimatedUdpListenerCount", nullptr},
+		{0xd24d4849, "SystemNative_Socket", nullptr},
+		{0xd298c3b3, "SystemNative_Write", nullptr},
+		{0xd378ba49, "CryptoNative_EvpMd5", nullptr},
+		{0xd473c64c, "SystemNative_SetReceiveTimeout", nullptr},
+		{0xd4b91180, "SystemNative_ForkAndExecProcess", nullptr},
+		{0xd6d7b4fb, "AndroidCryptoNative_X509ChainGetCertificateCount", nullptr},
+		{0xd71d8c66, "AndroidCryptoNative_Aes256Ecb", nullptr},
+		{0xd7ee326b, "AndroidCryptoNative_EcKeyDestroy", nullptr},
+		{0xd818a523, "AndroidCryptoNative_CipherCtxSetPadding", nullptr},
+		{0xd9458396, "BrotliDecoderCreateInstance", nullptr},
+		{0xda040de4, "GlobalizationNative_EndsWith", nullptr},
+		{0xdaaa19b2, "SystemNative_GetAllMountPoints", nullptr},
+		{0xdac67152, "AndroidCryptoNative_X509StoreAddCertificate", nullptr},
+		{0xdad29aeb, "AndroidCryptoNative_CipherUpdate", nullptr},
+		{0xdaf0460a, "SystemNative_SendFile", nullptr},
+		{0xdbbf4917, "AndroidCryptoNative_SSLStreamGetCipherSuite", nullptr},
+		{0xdbdce4ef, "BrotliDefaultAllocFunc", nullptr},
+		{0xdbe13a57, "SystemNative_GetCpuUtilization", nullptr},
+		{0xdc3cbeec, "CryptoNative_HmacOneShot", nullptr},
+		{0xdcaddb21, "AndroidCryptoNative_GetECKeyParameters", nullptr},
+		{0xdd274c15, "AndroidCryptoNative_RsaPublicEncrypt", nullptr},
+		{0xdd445632, "AndroidCryptoNative_X509ChainValidate", nullptr},
+		{0xddd58443, "SystemNative_PReadV", nullptr},
+		{0xdea9b9dc, "SystemNative_EnablePosixSignalHandling", nullptr},
+		{0xdf0260d8, "GlobalizationNative_GetLocaleInfoString", nullptr},
+		{0xdf4f1977, "AndroidCryptoNative_X509PublicKey", nullptr},
+		{0xdf5d3dc8, "GlobalizationNative_GetDefaultLocaleName", nullptr},
+		{0xdf80df75, "SystemNative_iOSSupportVersion", nullptr},
+		{0xe121bac7, "SystemNative_GetPeerID", nullptr},
+		{0xe169faa6, "AndroidCryptoNative_SSLStreamSetApplicationProtocols", nullptr},
+		{0xe1b8b44f, "SystemNative_Send", nullptr},
+		{0xe2a0d0de, "SystemNative_GetSpaceInfoForMountPoint", nullptr},
+		{0xe4a78efb, "SystemNative_SetPriority", nullptr},
+		{0xe4dba4f6, "SystemNative_GetCwd", nullptr},
+		{0xe4f87d25, "AndroidCryptoNative_SSLStreamVerifyHostname", nullptr},
+		{0xe50c82b4, "SystemNative_CreateThread", nullptr},
+		{0xe58ed8fe, "SystemNative_PlatformSupportsDualModeIPv4PacketInfo", nullptr},
+		{0xe5ef37b3, "AndroidCryptoNative_DsaSignatureFieldSize", nullptr},
+		{0xe70a3634, "GlobalizationNative_GetLocaleName", nullptr},
+		{0xe770cb3f, "SystemNative_CopyFile", nullptr},
+		{0xe7a9a106, "CompressionNative_InflateInit2_", nullptr},
+		{0xe7bd8dd1, "AndroidCryptoNative_EcKeyCreateByKeyParameters", nullptr},
+		{0xe890cf58, "AndroidCryptoNative_EcDsaSign", nullptr},
+		{0xe8b2ec8d, "BrotliDecoderErrorString", nullptr},
+		{0xe972fbd9, "SystemNative_GetEGid", nullptr},
+		{0xe9bc4e53, "SystemNative_SNPrintF_1S", nullptr},
+		{0xea86f52f, "BrotliEncoderTakeOutput", nullptr},
+		{0xeb0d0522, "SystemNative_LowLevelMonitor_Wait", nullptr},
+		{0xebacbf92, "AndroidCryptoNative_Aes128Cfb8", nullptr},
+		{0xec31140d, "BrotliDecoderHasMoreOutput", nullptr},
+		{0xec51a1b4, "SystemNative_LChflags", nullptr},
+		{0xed6cc182, "CryptoNative_EvpMdCtxCreate", nullptr},
+		{0xee74a5ad, "AndroidCryptoNative_DsaSizeP", nullptr},
+		{0xef48c2eb, "CryptoNative_EvpDigestReset", nullptr},
+		{0xef5890c7, "AndroidCryptoNative_Des3Cbc", nullptr},
+		{0xefb38c9f, "SystemNative_Poll", nullptr},
+		{0xefd277f7, "CryptoNative_HmacFinal", nullptr},
+		{0xf06b440b, "AndroidCryptoNative_DsaSizeSignature", nullptr},
+		{0xf0919525, "AndroidCryptoNative_CipherReset", nullptr},
+		{0xf0e499c4, "SystemNative_PRead", nullptr},
+		{0xf1bb5b47, "SystemNative_ReadLink", nullptr},
+		{0xf23e6314, "AndroidCryptoNative_RsaCreate", nullptr},
+		{0xf2a49cf0, "SystemNative_CloseSocketEventPort", nullptr},
+		{0xf39b1c3a, "SystemNative_GetCryptographicallySecureRandomBytes", nullptr},
+		{0xf3b9c879, "AndroidCryptoNative_X509Encode", nullptr},
+		{0xf432ab33, "SystemNative_CloseDir", nullptr},
+		{0xf4a5a1c8, "SystemNative_SysLog", nullptr},
+		{0xf500c9d3, "SystemNative_GetActiveUdpListeners", nullptr},
+		{0xf57828fb, "SystemNative_IsATty", nullptr},
+		{0xf5918f53, "SystemNative_GetSocketAddressSizes", nullptr},
+		{0xf6141499, "AndroidCryptoNative_X509ChainBuild", nullptr},
+		{0xf629d20f, "SystemNative_Shutdown", nullptr},
+		{0xf6b01c6b, "SystemNative_FreeEnviron", nullptr},
+		{0xf6bfedad, "SystemNative_ReadEvents", nullptr},
+		{0xf91cf365, "AndroidCryptoNative_Aes128Ccm", nullptr},
+		{0xf94a4828, "SystemNative_GetEUid", nullptr},
+		{0xf993f426, "SystemNative_Free", nullptr},
+		{0xfa97914b, "SystemNative_MMap", nullptr},
+		{0xfad61722, "AndroidCryptoNative_X509StoreAddCertificateWithPrivateKey", nullptr},
+		{0xfae25aa7, "GlobalizationNative_LastIndexOf", nullptr},
+		{0xfb89157f, "SystemNative_GetGroups", nullptr},
+		{0xfc83423c, "SystemNative_GetRawSockOpt", nullptr},
+		{0xfd9099cc, "SystemNative_GetUInt64OSThreadId", nullptr},
+		{0xfe2f2c47, "SystemNative_DrainAutoreleasePool", nullptr},
+		{0xfeb6c5c7, "SystemNative_WaitPidExitedNoHang", nullptr},
+		{0xff3b4cfa, "SystemNative_GetFileSystemType", nullptr},
+		{0xff975200, "SystemNative_Realloc", nullptr},
+	}};
+
+
+constexpr hash_t java_interop_library_hash = 0x6e36e350;
+constexpr hash_t xa_internal_api_library_hash = 0x13c9bd62;
+constexpr hash_t android_liblog_library_hash = 0xa70c9969;
+constexpr hash_t system_native_library_hash = 0x5b9ade60;
+constexpr hash_t system_io_compression_native_library_hash = 0xafe3142c;
+constexpr hash_t system_security_cryptography_native_android_library_hash = 0x93625cd;
+constexpr hash_t system_globalization_native_library_hash = 0xa66f1e5a;
+#endif
+
+constexpr size_t internal_pinvokes_count = 10;
+constexpr size_t dotnet_pinvokes_count = 477;
+} // end of anonymous namespace
diff --git a/src/native/clr/host/typemap.cc b/src/native/clr/host/typemap.cc
new file mode 100644
index 00000000000..8f1df3cce0f
--- /dev/null
+++ b/src/native/clr/host/typemap.cc
@@ -0,0 +1,319 @@
+#include <array>
+
+#include <host/typemap.hh>
+#include <runtime-base/timing-internal.hh>
+#include <runtime-base/search.hh>
+#include <runtime-base/util.hh>
+#include <shared/xxhash.hh>
+#include <xamarin-app.hh>
+
+using namespace xamarin::android;
+
+namespace {
+	class MonoGuidString
+	{
+		static inline constexpr size_t MVID_SIZE = 16;
+		static inline constexpr size_t NUM_HYPHENS = 4;
+		static inline constexpr size_t BUF_SIZE = (MVID_SIZE * 2) + NUM_HYPHENS + 1;
+
+	public:
+		explicit MonoGuidString (const uint8_t *mvid) noexcept
+		{
+			if (mvid == nullptr) {
+				_ascii_form[0] = '\0';
+				return;
+			}
+
+			// In the caller we trust, we have no way to validate the size here
+			auto to_hex = [this, &mvid] (size_t &dest_idx, size_t src_idx) {
+				Util::to_hex (mvid[src_idx], _ascii_form[dest_idx], _ascii_form[dest_idx + 1]);
+				dest_idx += 2;
+			};
+
+			auto hyphen = [this] (size_t &dest_idx) {
+				_ascii_form[dest_idx++] = '-';
+			};
+
+			size_t dest_idx = 0;
+			to_hex (dest_idx, 3); to_hex (dest_idx, 2); to_hex (dest_idx, 1); to_hex (dest_idx, 0);
+			hyphen (dest_idx);
+
+			to_hex (dest_idx, 5); to_hex (dest_idx, 4);
+			hyphen (dest_idx);
+
+			to_hex (dest_idx, 7); to_hex (dest_idx, 6);
+			hyphen (dest_idx);
+
+			to_hex (dest_idx, 8); to_hex (dest_idx, 9);
+			hyphen (dest_idx);
+
+			to_hex (dest_idx, 10); to_hex (dest_idx, 11); to_hex (dest_idx, 12);
+			to_hex (dest_idx, 13); to_hex (dest_idx, 14); to_hex (dest_idx, 15);
+
+			_ascii_form[_ascii_form.size () - 1] = '\0';
+		}
+
+		auto c_str () const noexcept -> const char*
+		{
+			return _ascii_form.data ();
+		}
+
+	private:
+		std::array<char, BUF_SIZE> _ascii_form;
+	};
+}
+
+#if defined(DEBUG)
+[[gnu::always_inline]]
+auto TypeMapper::typemap_managed_to_java_debug (const char *typeName, const uint8_t *mvid) noexcept -> const char*
+{
+	Helpers::abort_application ("TypeMap support for Debug builds not implemented yet"sv);
+}
+#endif // def DEBUG
+
+#if defined(RELEASE)
+[[gnu::always_inline]]
+auto TypeMapper::compare_mvid (const uint8_t *mvid, TypeMapModule const& module) noexcept -> int
+{
+	return memcmp (module.module_uuid, mvid, sizeof(module.module_uuid));
+}
+
+[[gnu::always_inline]]
+auto TypeMapper::find_module_entry (const uint8_t *mvid, const TypeMapModule *entries, size_t entry_count) noexcept -> const TypeMapModule*
+{
+	if (entries == nullptr || mvid == nullptr) [[unlikely]] {
+		return nullptr;
+	}
+
+	auto equal = [](TypeMapModule const& entry, const uint8_t *key) -> bool { return compare_mvid (key, entry) == 0; };
+	auto less_than = [](TypeMapModule const& entry, const uint8_t *key) -> bool { return compare_mvid (key, entry) < 0; };
+	ssize_t idx = Search::binary_search<TypeMapModule, const uint8_t*, equal, less_than> (mvid, entries, entry_count);
+	if (idx >= 0) [[likely]] {
+		return &entries[idx];
+	}
+
+	return nullptr;
+}
+
+[[gnu::always_inline]]
+auto TypeMapper::find_managed_to_java_map_entry (hash_t name_hash, const TypeMapModuleEntry *map, size_t entry_count) noexcept -> const TypeMapModuleEntry*
+{
+	if (map == nullptr) {
+		return nullptr;
+	};
+
+	auto equal = [](TypeMapModuleEntry const& entry, hash_t key) -> bool { return entry.managed_type_name_hash == key; };
+	auto less_than = [](TypeMapModuleEntry const& entry, hash_t key) -> bool { return entry.managed_type_name_hash < key; };
+	ssize_t idx = Search::binary_search<TypeMapModuleEntry, equal, less_than> (name_hash, map, entry_count);
+	if (idx >= 0) [[likely]] {
+		return &map[idx];
+	}
+
+	return nullptr;
+}
+
+[[gnu::always_inline]]
+auto TypeMapper::typemap_managed_to_java_release (const char *typeName, const uint8_t *mvid) noexcept -> const char*
+{
+	const TypeMapModule *match = find_module_entry (mvid, managed_to_java_map, managed_to_java_map_module_count);
+	if (match == nullptr) {
+		if (mvid == nullptr) {
+			log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"sv);
+		} else {
+			log_info (LOG_ASSEMBLY, "typemap: module matching MVID [{}] not found."sv, MonoGuidString (mvid).c_str ());
+		}
+		return nullptr;
+	}
+
+	log_debug (LOG_ASSEMBLY, "typemap: found module matching MVID [{}]"sv, MonoGuidString (mvid).c_str ());
+	hash_t name_hash = xxhash::hash (typeName, strlen (typeName));
+
+	const TypeMapModuleEntry *entry = find_managed_to_java_map_entry (name_hash, match->map, match->entry_count);
+	if (entry == nullptr) [[unlikely]] {
+		if (match->map == nullptr) [[unlikely]] {
+			log_warn (LOG_ASSEMBLY, "typemap: module with MVID [{}] has no associated type map.", MonoGuidString (mvid).c_str ());
+			return nullptr;
+		}
+
+		if (match->duplicate_count > 0 && match->duplicate_map != nullptr) {
+			log_debug (
+				LOG_ASSEMBLY,
+				"typemap: searching module [{}] duplicate map for type '{}' (hash {:x})",
+				MonoGuidString (mvid).c_str (),
+				optional_string (typeName),
+				name_hash
+			);
+			entry = find_managed_to_java_map_entry (name_hash, match->duplicate_map, match->duplicate_count);
+		}
+
+		if (entry == nullptr) {
+			log_warn (
+				LOG_ASSEMBLY,
+				"typemap: managed type '{}' (hash {:x}) not found in module [{}] ({}).",
+				optional_string (typeName),
+				name_hash,
+				MonoGuidString (mvid).c_str (),
+				optional_string (managed_assembly_names[match->assembly_name_index])
+			);
+			return nullptr;
+		}
+	}
+
+	if (entry->java_map_index >= java_type_count) [[unlikely]] {
+		log_warn (
+			LOG_ASSEMBLY,
+			"typemap: managed type '{}' (hash {:x}) in module [{}] ({}) has invalid Java type index {}",
+			optional_string (typeName),
+			name_hash,
+			MonoGuidString (mvid).c_str (),
+			optional_string (managed_assembly_names[match->assembly_name_index]),
+			entry->java_map_index
+		);
+		return nullptr;
+	}
+
+	TypeMapJava const& java_entry = java_to_managed_map[entry->java_map_index];
+	if (java_entry.java_name_index >= java_type_count) [[unlikely]] {
+		log_warn (
+			LOG_ASSEMBLY,
+			"typemap: managed type '{}' (hash {:x}) in module [{}] ({}) points to invalid Java type at index {} (invalid type name index {})",
+			optional_string (typeName),
+			name_hash,
+			MonoGuidString (mvid).c_str (),
+			optional_string (managed_assembly_names[match->assembly_name_index]),
+			entry->java_map_index,
+			java_entry.java_name_index
+		);
+
+		return nullptr;
+	}
+
+	const char *ret = java_type_names[java_entry.java_name_index];
+	if (ret == nullptr) [[unlikely]] {
+		log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index {}", entry->java_map_index);
+	}
+
+	log_debug (
+		LOG_ASSEMBLY,
+		"typemap: managed type '{}' (hash {:x}) in module [{}] ({}) corresponds to Java type '{}'",
+		optional_string (typeName),
+		name_hash,
+		MonoGuidString (mvid).c_str (),
+		optional_string (managed_assembly_names[match->assembly_name_index]),
+		ret
+	);
+
+	return ret;
+}
+#endif // def RELEASE
+
+[[gnu::flatten]]
+auto TypeMapper::typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept -> const char*
+{
+	size_t total_time_index;
+	if (FastTiming::enabled ()) [[unlikely]] {
+		//timing = new Timing ();
+		total_time_index = internal_timing.start_event (TimingEventKind::ManagedToJava);
+	}
+
+	if (typeName == nullptr) [[unlikely]] {
+		log_warn (LOG_ASSEMBLY, "typemap: type name not specified in typemap_managed_to_java");
+		return nullptr;
+	}
+
+	const char *ret = nullptr;
+#if defined(RELEASE)
+	ret = typemap_managed_to_java_release (typeName, mvid);
+#else
+	ret = typemap_managed_to_java_debug (typeName, mvid);
+#endif
+
+	if (FastTiming::enabled ()) [[unlikely]] {
+		internal_timing.end_event (total_time_index);
+	}
+
+	return ret;
+}
+
+#if defined(DEBUG)
+[[gnu::flatten]]
+auto TypeMapper::typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept -> bool
+{
+	Helpers::abort_application ("typemap_java_to_managed not implemented for debug builds yet");
+}
+#else // def DEBUG
+
+[[gnu::always_inline]]
+auto TypeMapper::find_java_to_managed_entry (hash_t name_hash) noexcept -> const TypeMapJava*
+{
+	ssize_t idx = Search::binary_search (name_hash, java_to_managed_hashes, java_type_count);
+	if (idx < 0) [[unlikely]] {
+		return nullptr;
+	}
+
+	return &java_to_managed_map[idx];
+}
+
+[[gnu::flatten]]
+auto TypeMapper::typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept -> bool
+{
+	if (java_type_name == nullptr || assembly_name == nullptr || managed_type_token_id == nullptr) [[unlikely]] {
+		if (java_type_name == nullptr) {
+			log_warn (
+				LOG_ASSEMBLY,
+				"typemap: required parameter `{}` not passed to {}",
+				"java_type_name"sv,
+				__PRETTY_FUNCTION__
+			);
+		}
+
+		if (assembly_name == nullptr) {
+			log_warn (
+				LOG_ASSEMBLY,
+				"typemap: required parameter `{}` not passed to {}",
+				"assembly_name"sv,
+				__PRETTY_FUNCTION__
+			);
+		}
+
+		if (managed_type_token_id == nullptr) {
+			log_warn (
+				LOG_ASSEMBLY,
+				"typemap: required parameter `{}` not passed to {}",
+				"managed_type_token_id"sv,
+				__PRETTY_FUNCTION__
+			);
+		}
+
+		return false;
+	}
+
+	hash_t name_hash = xxhash::hash (java_type_name, strlen (java_type_name));
+	TypeMapJava const* java_entry = find_java_to_managed_entry (name_hash);
+	if (java_entry == nullptr) {
+		log_info (
+			LOG_ASSEMBLY,
+			"typemap: unable to find mapping to a managed type from Java type '{}' (hash {:x})",
+			optional_string (java_type_name),
+			name_hash
+		);
+
+		return false;
+	}
+
+	TypeMapModule const &module = managed_to_java_map[java_entry->module_index];
+	*assembly_name = managed_assembly_names[module.assembly_name_index];
+	*managed_type_token_id = java_entry->managed_type_token_id;
+
+	log_debug (
+		LOG_ASSEMBLY,
+		"Java type '{}' corresponds to managed type '{}' (token 0x{:x} in assembly '{}')",
+		optional_string (java_type_name),
+		optional_string (managed_type_names[java_entry->managed_type_name_index]),
+		*managed_type_token_id,
+		optional_string (*assembly_name)
+	);
+
+	return true;
+}
+#endif // ndef DEBUG
diff --git a/src/native/clr/include/host/assembly-store.hh b/src/native/clr/include/host/assembly-store.hh
new file mode 100644
index 00000000000..4648231e958
--- /dev/null
+++ b/src/native/clr/include/host/assembly-store.hh
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <mutex>
+#include <string_view>
+#include <tuple>
+
+#include <xamarin-app.hh>
+#include <runtime-base/strings.hh>
+
+namespace xamarin::android {
+	class AssemblyStore
+	{
+	public:
+		static auto open_assembly (std::string_view const& name, int64_t &size) noexcept -> void*;
+
+		static void map (int fd, std::string_view const& apk_path, std::string_view const& store_path, uint32_t offset, uint32_t size) noexcept;
+
+		static void map (int fd, std::string_view const& file_path, uint32_t offset, uint32_t size) noexcept
+		{
+			map (fd, {}, file_path, offset, size);
+		}
+
+	private:
+		static void set_assembly_data_and_size (uint8_t* source_assembly_data, uint32_t source_assembly_data_size, uint8_t*& dest_assembly_data, uint32_t& dest_assembly_data_size) noexcept;
+
+		// Returns a tuple of <assembly_data_pointer, data_size>
+		static auto get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData const& e, std::string_view const& name, bool force_rw = false) noexcept -> std::tuple<uint8_t*, uint32_t>;
+		static auto find_assembly_store_entry (hash_t hash, const AssemblyStoreIndexEntry *entries, size_t entry_count) noexcept -> const AssemblyStoreIndexEntry*;
+
+	private:
+		static inline AssemblyStoreIndexEntry *assembly_store_hashes = nullptr;
+		static inline std::mutex  assembly_decompress_mutex {};
+	};
+}
diff --git a/src/native/clr/include/host/host-jni.hh b/src/native/clr/include/host/host-jni.hh
new file mode 100644
index 00000000000..4904644ebd8
--- /dev/null
+++ b/src/native/clr/include/host/host-jni.hh
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <jni.h>
+
+extern "C" {
+	/*
+	 * Class:     mono_android_Runtime
+	 * Method:    initInternal
+	 * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;ILjava/lang/ClassLoader;[Ljava/lang/String;IZZ)V
+	 */
+	JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal(JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jint, jobject, jobjectArray, jboolean, jboolean);
+
+	/*
+	 * Class:     mono_android_Runtime
+	 * Method:    dumpTimingData
+	 * Signature: ()V
+	 */
+	JNIEXPORT void JNICALL Java_mono_android_Runtime_dumpTimingData (JNIEnv *, jclass);
+
+	/*
+	 * Class:     mono_android_Runtime
+	 * Method:    initInternal
+	 * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;ILjava/lang/ClassLoader;[Ljava/lang/String;IZZ)V
+	 */
+	JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jint, jobject, jobjectArray, jboolean, jboolean);
+
+	/*
+	 * Class:     mono_android_Runtime
+	 * Method:    notifyTimeZoneChanged
+	 * Signature: ()V
+	 */
+	JNIEXPORT void JNICALL Java_mono_android_Runtime_notifyTimeZoneChanged (JNIEnv *, jclass);
+
+	/*
+	 * Class:     mono_android_Runtime
+	 * Method:    propagateUncaughtException
+	 * Signature: (Ljava/lang/Thread;Ljava/lang/Throwable;)V
+	 */
+	JNIEXPORT void JNICALL Java_mono_android_Runtime_propagateUncaughtException (JNIEnv *, jclass, jobject, jthrowable);
+
+	/*
+	 * Class:     mono_android_Runtime
+	 * Method:    register
+	 * Signature: (Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;)V
+	 */
+	JNIEXPORT void JNICALL Java_mono_android_Runtime_register (JNIEnv *, jclass, jstring, jclass, jstring);
+
+}
diff --git a/src/native/clr/include/host/host-util.hh b/src/native/clr/include/host/host-util.hh
new file mode 100644
index 00000000000..8fb9edc0873
--- /dev/null
+++ b/src/native/clr/include/host/host-util.hh
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <jni.h>
+
+namespace xamarin::android {
+	class HostUtil
+	{
+	public:
+		static auto get_class_from_runtime_field (JNIEnv *env, jclass runtime, const char *name, bool make_gref) noexcept -> jclass;
+	};
+}
diff --git a/src/native/clr/include/host/host.hh b/src/native/clr/include/host/host.hh
new file mode 100644
index 00000000000..a1e70ca8135
--- /dev/null
+++ b/src/native/clr/include/host/host.hh
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <array>
+#include <string_view>
+
+#include <jni.h>
+#include <host_runtime_contract.h>
+
+#include <runtime-base/jni-wrappers.hh>
+#include "../runtime-base/timing.hh"
+#include "../shared/log_types.hh"
+#include "managed-interface.hh"
+
+namespace xamarin::android {
+	class Host
+	{
+	public:
+		static auto Java_JNI_OnLoad (JavaVM *vm, void *reserved) noexcept -> jint;
+		static void Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava,
+			jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader,
+			jobjectArray assembliesJava, jboolean isEmulator, jboolean haveSplitApks) noexcept;
+		static void Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, jclass nativeClass, jstring methods) noexcept;
+
+		static auto get_java_class_name_for_TypeManager (jclass klass) noexcept -> char*;
+
+		static auto get_timing () -> Timing*
+		{
+			return _timing.get ();
+		}
+
+	private:
+		static void create_xdg_directory (jstring_wrapper& home, size_t home_len, std::string_view const& relative_path, std::string_view const& environment_variable_name) noexcept;
+		static void create_xdg_directories_and_environment (jstring_wrapper &homeDir) noexcept;
+		static auto zip_scan_callback (std::string_view const& apk_path, int apk_fd, dynamic_local_string<SENSIBLE_PATH_MAX> const& entry_name, uint32_t offset, uint32_t size) -> bool;
+		static void gather_assemblies_and_libraries (jstring_array_wrapper& runtimeApks, bool have_split_apks);
+
+		static size_t clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, void *contract_context) noexcept;
+		static bool clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept;
+		static const void* clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept;
+		static void clr_error_writer (const char *message) noexcept;
+
+		static auto create_delegate (
+			std::string_view const& assembly_name, std::string_view const& type_name,
+			std::string_view const& method_name) noexcept -> void*;
+
+	private:
+		static inline void *clr_host = nullptr;
+		static inline unsigned int domain_id = 0;
+		static inline std::unique_ptr<Timing> _timing{};
+		static inline bool found_assembly_store = false;
+		static inline jnienv_register_jni_natives_fn jnienv_register_jni_natives = nullptr;
+
+		static inline JavaVM *jvm = nullptr;
+		static inline jmethodID Class_getName = nullptr;
+
+		static inline host_runtime_contract runtime_contract{
+			.size = sizeof(host_runtime_contract),
+			.context = nullptr,
+			.get_runtime_property = clr_get_runtime_property,
+			.bundle_probe = nullptr,
+			.pinvoke_override = clr_pinvoke_override,
+			.external_assembly_probe = clr_external_assembly_probe,
+		};
+
+		// Enough to fit 0xffffffffffffffff + terminating NUL
+		static inline std::array<char, 19> host_contract_ptr_buffer{};
+	};
+}
diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh
new file mode 100644
index 00000000000..0bce2a180fb
--- /dev/null
+++ b/src/native/clr/include/host/os-bridge.hh
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <cstdio>
+
+#include <jni.h>
+
+#include "../runtime-base/logger.hh"
+
+namespace xamarin::android {
+	class OSBridge
+	{
+	public:
+		static void initialize_on_onload (JavaVM *vm, JNIEnv *env) noexcept;
+		static void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept;
+		static auto lref_to_gref (JNIEnv *env, jobject lref) noexcept -> jobject;
+
+		static auto get_gc_gref_count () noexcept -> int
+		{
+			return gc_gref_count;
+		}
+
+		static auto get_gc_weak_gref_count () noexcept -> int
+		{
+			return gc_weak_gref_count;
+		}
+
+		static void _monodroid_gref_log (const char *message) noexcept;
+		static auto _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, int from_writable) noexcept -> int;
+		static void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept;
+
+		static auto ensure_jnienv () noexcept -> JNIEnv*
+		{
+			JNIEnv *env = nullptr;
+			jvm->GetEnv ((void**)&env, JNI_VERSION_1_6);
+			if (env == nullptr) {
+				// TODO: attach to the runtime thread here
+				jvm->GetEnv ((void**)&env, JNI_VERSION_1_6);
+				abort_unless (env != nullptr, "Unable to get a valid pointer to JNIEnv");
+			}
+
+			return env;
+		}
+
+	private:
+		static auto _monodroid_gref_inc () noexcept -> int;
+		static auto _monodroid_gref_dec () noexcept -> int;
+		static auto _get_stack_trace_line_end (char *m) noexcept -> char*;
+		static void _write_stack_trace (FILE *to, char *from, LogCategories = LOG_NONE) noexcept;
+
+	private:
+		static inline JavaVM *jvm = nullptr;
+		static inline jclass GCUserPeer_class = nullptr;
+		static inline jmethodID GCUserPeer_ctor = nullptr;
+
+		static inline int gc_gref_count = 0;
+		static inline int gc_weak_gref_count = 0;
+	};
+}
diff --git a/src/native/clr/include/host/pinvoke-override.hh b/src/native/clr/include/host/pinvoke-override.hh
new file mode 100644
index 00000000000..e1b7afebd95
--- /dev/null
+++ b/src/native/clr/include/host/pinvoke-override.hh
@@ -0,0 +1,223 @@
+#pragma once
+
+#include <mutex>
+#include <string>
+
+// NDEBUG causes robin_map.h not to include <iostream> which, in turn, prevents indirect inclusion of <mutex>. <mutex>
+// conflicts with our std::mutex definition in cppcompat.hh
+#if !defined (NDEBUG)
+#define NDEBUG
+#define NDEBUG_UNDEFINE
+#endif
+
+// hush some compiler warnings
+#if defined (__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#endif // __clang__
+
+#include <tsl/robin_map.h>
+
+#if defined (__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined (NDEBUG_UNDEFINE)
+#undef NDEBUG
+#undef NDEBUG_UNDEFINE
+#endif
+
+#include "../runtime-base/monodroid-dl.hh"
+#include <shared/xxhash.hh>
+
+namespace xamarin::android {
+	struct PinvokeEntry
+	{
+		hash_t      hash;
+		const char *name;
+		void       *func;
+	};
+
+	struct string_hash
+	{
+		[[gnu::always_inline]]
+		xamarin::android::hash_t operator() (std::string const& s) const noexcept
+		{
+			return xamarin::android::xxhash::hash (s.c_str (), s.length ());
+		}
+	};
+
+	class PinvokeOverride
+	{
+		using pinvoke_api_map = tsl::robin_map<
+			std::string,
+			void*,
+			string_hash,
+			std::equal_to<std::string>,
+			std::allocator<std::pair<std::string, void*>>,
+			true
+		>;
+
+		using pinvoke_api_map_ptr = pinvoke_api_map*;
+		using pinvoke_library_map = tsl::robin_map<
+			std::string,
+			pinvoke_api_map_ptr,
+			string_hash,
+			std::equal_to<std::string>,
+			std::allocator<std::pair<std::string, pinvoke_api_map_ptr>>,
+			true
+		>;
+
+		static inline constexpr pinvoke_library_map::size_type LIBRARY_MAP_INITIAL_BUCKET_COUNT = 1uz;
+
+	public:
+		[[gnu::always_inline]]
+		static auto load_library_symbol (const char *library_name, const char *symbol_name, void **dso_handle = nullptr) noexcept -> void*
+		{
+			void *lib_handle = dso_handle == nullptr ? nullptr : *dso_handle;
+
+			if (lib_handle == nullptr) {
+				// We're being called as part of the p/invoke mechanism, we don't need to look in the AOT cache
+				constexpr bool PREFER_AOT_CACHE = false;
+				lib_handle = MonodroidDl::monodroid_dlopen (library_name, microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY, PREFER_AOT_CACHE);
+				if (lib_handle == nullptr) {
+					log_warn (LOG_ASSEMBLY, "Shared library '{}' not loaded, p/invoke '{}' may fail", optional_string (library_name), optional_string (symbol_name));
+					return nullptr;
+				}
+
+				if (dso_handle != nullptr) {
+					void *expected_null = nullptr;
+					if (!__atomic_compare_exchange (dso_handle, &expected_null, &lib_handle, false /* weak */, __ATOMIC_ACQUIRE /* success_memorder */, __ATOMIC_RELAXED /* xxxfailure_memorder */)) {
+						log_debug (LOG_ASSEMBLY, "Library '{}' handle already cached by another thread", optional_string (library_name));
+					}
+				}
+			}
+
+			void *entry_handle = MonodroidDl::monodroid_dlsym (lib_handle, symbol_name);
+			if (entry_handle == nullptr) {
+				log_warn (LOG_ASSEMBLY, "Symbol '{}' not found in shared library '{}', p/invoke may fail", optional_string (library_name), optional_string (symbol_name));
+				return nullptr;
+			}
+
+			return entry_handle;
+		}
+
+		static auto load_library_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map) noexcept -> void*
+		{
+			// Make sure some other thread hasn't just added the entry
+			auto iter = api_map->find (entrypoint_name);
+			if (iter != api_map->end () && iter->second != nullptr) {
+				return iter->second;
+			}
+
+			void *entry_handle = load_library_symbol (library_name.c_str (), entrypoint_name.c_str ());
+			if (entry_handle == nullptr) {
+				// error already logged
+				return nullptr;
+			}
+
+			log_debug (LOG_ASSEMBLY, "Caching p/invoke entry {} @ {}", library_name, entrypoint_name);
+			(*api_map)[entrypoint_name] = entry_handle;
+			return entry_handle;
+		}
+
+		static void load_library_entry (const char *library_name, const char *entrypoint_name, PinvokeEntry &entry, void **dso_handle) noexcept
+		{
+			void *entry_handle = load_library_symbol (library_name, entrypoint_name, dso_handle);
+			void *expected_null = nullptr;
+
+			bool already_loaded = !__atomic_compare_exchange (
+				/* ptr */              &entry.func,
+				/* expected */         &expected_null,
+				/* desired */          &entry_handle,
+				/* weak */              false,
+				/* success_memorder */  __ATOMIC_ACQUIRE,
+				/* failure_memorder */  __ATOMIC_RELAXED
+			);
+
+			if (already_loaded) {
+				log_debug (LOG_ASSEMBLY, "Entry '{}' from library '{}' already loaded by another thread", entrypoint_name, library_name);
+			}
+		}
+
+		static auto fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, hash_t entrypoint_name_hash, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void*
+		{
+			auto iter = api_map->find (entrypoint_name, entrypoint_name_hash);
+			if (iter != api_map->end () && iter->second != nullptr) {
+				return iter->second;
+			}
+
+			if (!need_lock) {
+				return load_library_entry (library_name, entrypoint_name, api_map);
+			}
+
+			StartupAwareLock lock (pinvoke_map_write_lock);
+			return load_library_entry (library_name, entrypoint_name, api_map);
+		}
+
+		[[gnu::always_inline]]
+		static auto find_pinvoke_address (hash_t hash, const PinvokeEntry *entries, size_t entry_count) noexcept -> PinvokeEntry*
+		{
+			while (entry_count > 0uz) {
+				const size_t mid = entry_count / 2uz;
+				const PinvokeEntry *const ret = entries + mid;
+				const std::strong_ordering result = hash <=> ret->hash;
+
+				if (result < 0) {
+					entry_count = mid;
+				} else if (result > 0) {
+					entries = ret + 1;
+					entry_count -= mid + 1uz;
+				} else {
+					return const_cast<PinvokeEntry*>(ret);
+				}
+			}
+
+			return nullptr;
+		}
+
+		[[gnu::always_inline, gnu::flatten]]
+		static auto handle_other_pinvoke_request (const char *library_name, hash_t library_name_hash, const char *entrypoint_name, hash_t entrypoint_name_hash) noexcept -> void*
+		{
+			std::string lib_name {library_name};
+			std::string entry_name {entrypoint_name};
+
+			auto iter = other_pinvoke_map.find (lib_name, library_name_hash);
+			void *handle = nullptr;
+			if (iter == other_pinvoke_map.end ()) {
+				StartupAwareLock lock (pinvoke_map_write_lock);
+
+				pinvoke_api_map_ptr lib_map;
+				// Make sure some other thread hasn't just added the map
+				iter = other_pinvoke_map.find (lib_name, library_name_hash);
+				if (iter == other_pinvoke_map.end () || iter->second == nullptr) {
+					lib_map = new pinvoke_api_map (1);
+					other_pinvoke_map[lib_name] = lib_map;
+				} else {
+					lib_map = iter->second;
+				}
+
+				handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, lib_map, /* need_lock */ false);
+			} else {
+				if (iter->second == nullptr) [[unlikely]] {
+					log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '{}'", optional_string (library_name));
+					return nullptr; // fall back to `monodroid_dlopen`
+				}
+
+				handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, iter->second, /* need_lock */ true);
+			}
+
+			return handle;
+		}
+
+		static auto monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void*;
+
+	private:
+		static inline std::mutex          pinvoke_map_write_lock{};
+		static inline pinvoke_library_map other_pinvoke_map{};
+		static inline void *system_native_library_handle = nullptr;
+		static inline void *system_security_cryptography_native_android_library_handle = nullptr;
+		static inline void *system_io_compression_native_library_handle = nullptr;
+		static inline void *system_globalization_native_library_handle = nullptr;
+	};
+}
diff --git a/src/native/clr/include/host/typemap.hh b/src/native/clr/include/host/typemap.hh
new file mode 100644
index 00000000000..f695f0ce5d4
--- /dev/null
+++ b/src/native/clr/include/host/typemap.hh
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <cstdint>
+
+#include "../runtime-base/logger.hh"
+#include <shared/xxhash.hh>
+#include "../xamarin-app.hh"
+
+namespace xamarin::android {
+	class TypeMapper
+	{
+	public:
+		static auto typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept -> const char*;
+		static auto typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_doken_id) noexcept -> bool;
+
+	private:
+#if defined(RELEASE)
+		static auto compare_mvid (const uint8_t *mvid, TypeMapModule const& module) noexcept -> int;
+		static auto find_module_entry (const uint8_t *mvid, const TypeMapModule *entries, size_t entry_count) noexcept -> const TypeMapModule*;
+		static auto find_managed_to_java_map_entry (hash_t name_hash, const TypeMapModuleEntry *map, size_t entry_count) noexcept -> const TypeMapModuleEntry*;
+		static auto typemap_managed_to_java_release (const char *typeName, const uint8_t *mvid) noexcept -> const char*;
+
+		static auto find_java_to_managed_entry (hash_t name_hash) noexcept -> const TypeMapJava*;
+#else
+		static auto typemap_managed_to_java_debug (const char *typeName, const uint8_t *mvid) noexcept -> const char*;
+#endif
+	};
+}
diff --git a/src/native/clr/include/runtime-base/internal-pinvokes.hh b/src/native/clr/include/runtime-base/internal-pinvokes.hh
new file mode 100644
index 00000000000..44ba6fb99cb
--- /dev/null
+++ b/src/native/clr/include/runtime-base/internal-pinvokes.hh
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <jni.h>
+
+#include "logger.hh"
+
+int _monodroid_gref_get () noexcept;
+void _monodroid_gref_log (const char *message) noexcept;
+int _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, int from_writable) noexcept;
+void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from, int from_writable) noexcept;
+const char* clr_typemap_managed_to_java (const char *typeName, const uint8_t *mvid) noexcept;
+bool clr_typemap_java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept;
+void monodroid_log (xamarin::android::LogLevel level, LogCategories category, const char *message) noexcept;
+char* monodroid_TypeManager_get_java_class_name (jclass klass) noexcept;
+void monodroid_free (void *ptr) noexcept;
diff --git a/src/native/clr/include/runtime-base/timing-internal.hh b/src/native/clr/include/runtime-base/timing-internal.hh
index 6897c34df1d..185f786bff7 100644
--- a/src/native/clr/include/runtime-base/timing-internal.hh
+++ b/src/native/clr/include/runtime-base/timing-internal.hh
@@ -88,12 +88,15 @@ namespace xamarin::android {
 		static constexpr uint32_t ns_in_second = ms_in_second * ns_in_millisecond;
 
 	protected:
-		FastTiming () noexcept
+		void configure_for_use () noexcept
 		{
 			events.reserve (INITIAL_EVENT_VECTOR_SIZE);
 		}
 
 	public:
+		constexpr FastTiming () noexcept
+		{}
+
 		[[gnu::always_inline]]
 		static auto enabled () noexcept -> bool
 		{
@@ -462,5 +465,5 @@ namespace xamarin::android {
 		static inline bool immediate_logging = false;
 	};
 
-	extern FastTiming *internal_timing;
+	extern FastTiming internal_timing;
 }
diff --git a/src/native/clr/include/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh
index 78100e05a71..efa658fa782 100644
--- a/src/native/clr/include/runtime-base/util.hh
+++ b/src/native/clr/include/runtime-base/util.hh
@@ -41,12 +41,25 @@ namespace xamarin::android {
 
 	class Util
 	{
+		static constexpr inline std::array<char, 16> hex_map {
+			'0', '1', '2', '3', '4', '5', '6', '7',
+			'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
+		};
+
 	public:
 		static int create_directory (const char *pathname, mode_t mode);
 		static void create_public_directory (std::string_view const& dir);
 		static auto monodroid_fopen (std::string_view const& filename, std::string_view const& mode) noexcept -> FILE*;
 		static void set_world_accessable (std::string_view const& path);
 
+		// Puts higher half of the `value` byte as a hexadecimal character in `high_half` and
+		// the lower half in `low_half`
+		static void to_hex (uint8_t value, char &high_half, char &low_half) noexcept
+		{
+			high_half = hex_map[(value & 0xf0) >> 4];
+			low_half = hex_map[value & 0x0f];
+		}
+
 		static auto should_log (LogCategories category) noexcept -> bool
 		{
 			return (log_categories & category) != 0;
diff --git a/src/native/clr/include/xamarin-app.hh b/src/native/clr/include/xamarin-app.hh
index c65896eb93b..d2a7899dfe5 100644
--- a/src/native/clr/include/xamarin-app.hh
+++ b/src/native/clr/include/xamarin-app.hh
@@ -1,14 +1,14 @@
 // Dear Emacs, this is a -*- C++ -*- header
 #pragma once
 
+#include <array>
 #include <cstdint>
 
 #include <jni.h>
-#include <corehost/host_runtime_contract.h>
 
 #include <shared/xxhash.hh>
 
-static constexpr uint64_t FORMAT_TAG = 0x00035E6972616D58; // 'Xmari^XY' where XY is the format version
+static constexpr uint64_t FORMAT_TAG = 0x00045E6972616D58; // 'Xmari^XY' where XY is the format version
 static constexpr uint32_t COMPRESSED_DATA_MAGIC = 0x5A4C4158; // 'XALZ', little-endian
 static constexpr uint32_t ASSEMBLY_STORE_MAGIC = 0x41424158; // 'XABA', little-endian
 
@@ -74,8 +74,8 @@ struct TypeMap
 #else
 struct TypeMapModuleEntry
 {
-	uint32_t       type_token_id;
-	uint32_t       java_map_index;
+	xamarin::android::hash_t managed_type_name_hash;
+	uint32_t                 java_map_index;
 };
 
 struct TypeMapModule
@@ -83,19 +83,17 @@ struct TypeMapModule
 	uint8_t                   module_uuid[16];
 	uint32_t                  entry_count;
 	uint32_t                  duplicate_count;
+	uint32_t                  assembly_name_index;
 	TypeMapModuleEntry const *map;
 	TypeMapModuleEntry const *duplicate_map;
-	char const               *assembly_name;
-	uint8_t                  *image;
-	uint32_t                  java_name_width;
-	uint8_t                  *java_map;
 };
 
 struct TypeMapJava
 {
-	uint32_t module_index;
-	uint32_t type_token_id;
-	uint32_t java_name_index;
+	uint32_t  module_index;
+	uint32_t  managed_type_name_index;
+	uint32_t  managed_type_token_id;
+	uint32_t  java_name_index;
 };
 #endif
 
@@ -315,12 +313,14 @@ extern "C" {
 #if defined (DEBUG)
 	[[gnu::visibility("default")]] extern const TypeMap type_map; // MUST match src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGenerator.cs
 #else
-	[[gnu::visibility("default")]] extern const uint32_t map_module_count;
+	[[gnu::visibility("default")]] extern const uint32_t managed_to_java_map_module_count;
 	[[gnu::visibility("default")]] extern const uint32_t java_type_count;
 	[[gnu::visibility("default")]] extern const char* const java_type_names[];
-	[[gnu::visibility("default")]] extern TypeMapModule map_modules[];
-	[[gnu::visibility("default")]] extern const TypeMapJava map_java[];
-	[[gnu::visibility("default")]] extern const xamarin::android::hash_t map_java_hashes[];
+	[[gnu::visibility("default")]] extern const char* const managed_type_names[];
+	[[gnu::visibility("default")]] extern const char* const managed_assembly_names[];
+	[[gnu::visibility("default")]] extern TypeMapModule managed_to_java_map[];
+	[[gnu::visibility("default")]] extern const TypeMapJava java_to_managed_map[];
+	[[gnu::visibility("default")]] extern const xamarin::android::hash_t java_to_managed_hashes[];
 #endif
 
 	[[gnu::visibility("default")]] extern CompressedAssemblies compressed_assemblies;
@@ -341,7 +341,8 @@ extern "C" {
 	[[gnu::visibility("default")]] extern const RuntimeProperty runtime_properties[];
 	[[gnu::visibility("default")]] extern const RuntimePropertyIndexEntry runtime_property_index[];
 
-	[[gnu::visibility("default")]] extern const host_configuration_properties host_config_properties;
+	[[gnu::visibility("default")]] extern const char *init_runtime_property_names[];
+	[[gnu::visibility("default")]] extern char *init_runtime_property_values[];
 }
 
 //
diff --git a/src/native/clr/runtime-base/timing-internal.cc b/src/native/clr/runtime-base/timing-internal.cc
index fab5e4ed160..8df98638427 100644
--- a/src/native/clr/runtime-base/timing-internal.cc
+++ b/src/native/clr/runtime-base/timing-internal.cc
@@ -4,13 +4,13 @@
 using namespace xamarin::android;
 
 namespace xamarin::android {
-	FastTiming *internal_timing = nullptr;
+	FastTiming internal_timing;
 }
 
 void
 FastTiming::really_initialize (bool log_immediately) noexcept
 {
-	internal_timing = new FastTiming ();
+	internal_timing.configure_for_use ();
 	is_enabled = true;
 	immediate_logging = log_immediately;
 
diff --git a/src/native/clr/xamarin-app-stub/application_dso_stub.cc b/src/native/clr/xamarin-app-stub/application_dso_stub.cc
index 0e355298d40..34ee15f4c66 100644
--- a/src/native/clr/xamarin-app-stub/application_dso_stub.cc
+++ b/src/native/clr/xamarin-app-stub/application_dso_stub.cc
@@ -22,13 +22,14 @@ const TypeMap type_map = {
 	managed_to_java
 };
 #else
-const uint32_t map_module_count = 0;
+const uint32_t managed_to_java_map_module_count = 0;
 const uint32_t java_type_count = 0;
 const char* const java_type_names[] = {};
-
-TypeMapModule map_modules[] = {};
-const TypeMapJava map_java[] = {};
-const xamarin::android::hash_t map_java_hashes[] = {};
+const char* const managed_type_names[] = {};
+const char* const managed_assembly_names[] = {};
+TypeMapModule managed_to_java_map[] = {};
+const TypeMapJava java_to_managed_map[] = {};
+const xamarin::android::hash_t java_to_managed_hashes[] = {};
 #endif
 
 CompressedAssemblies compressed_assemblies = {
@@ -339,26 +340,10 @@ const RuntimePropertyIndexEntry runtime_property_index[] = {
 	},
 };
 
-namespace {
-	const host_configuration_property _host_configuration_properties_data[] = {
-		{
-			.name = u"test_string",
-			.value = u"string value",
-		},
-
-		{
-			.name = u"test_integer",
-			.value = u"23",
-		},
-
-		{
-			.name = u"test_boolean",
-			.value = u"true",
-		},
-	};
-}
+const char *init_runtime_property_names[] = {
+	"HOST_RUNTIME_CONTRACT",
+};
 
-const host_configuration_properties host_config_properties = {
-	.nitems = 3,
-	.data = _host_configuration_properties_data,
+char *init_runtime_property_values[] {
+	nullptr,
 };
diff --git a/src/native/cmake/ArchiveDSOStub.cmake b/src/native/cmake/ArchiveDSOStub.cmake
index f11b83372aa..8e4cdd2abac 100644
--- a/src/native/cmake/ArchiveDSOStub.cmake
+++ b/src/native/cmake/ArchiveDSOStub.cmake
@@ -1,3 +1,3 @@
 set(ARCHIVE_DSO_STUB_LIB_NAME "archive-dso-stub")
 set(ARCHIVE_DSO_STUB_LIB_FILE_NAME "lib${ARCHIVE_DSO_STUB_LIB_NAME}.so")
-set(XA_ARCHIVE_STUB_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/dsostubs/${ANDROID_RID}")
+set(XA_ARCHIVE_STUB_OUTPUT_DIRECTORY "${OUTPUT_PATH}/${ANDROID_RID}")
diff --git a/src/native/common/libstub/CMakeLists.txt b/src/native/common/libstub/CMakeLists.txt
index 70563594fc4..28ec03682c0 100644
--- a/src/native/common/libstub/CMakeLists.txt
+++ b/src/native/common/libstub/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(XA_LIBRARY_STUBS_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/libstubs/${ANDROID_RID}")
+set(XA_LIBRARY_STUBS_OUTPUT_DIRECTORY "${OUTPUT_PATH}/${ANDROID_RID}")
 
 set(XAMARIN_STUB_LIB_SOURCES
   stub.cc
diff --git a/src/native/native-clr.csproj b/src/native/native-clr.csproj
new file mode 100644
index 00000000000..2bcc54f139a
--- /dev/null
+++ b/src/native/native-clr.csproj
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.Build.NoTargets">
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <OutputType>Exe</OutputType>
+    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+
+    <!-- HACK HACK: until CoreCLR exists for all the targets, work only with arm64 -->
+    <AndroidSupportedTargetAotAbis>arm64</AndroidSupportedTargetAotAbis>
+    <AndroidSupportedTargetJitAbis>arm64-v8a</AndroidSupportedTargetJitAbis>
+  </PropertyGroup>
+
+  <Import Project="..\..\Configuration.props" />
+
+  <PropertyGroup>
+    <OutputPath>$(NativeRuntimeOutputRootDir)clr</OutputPath>
+    <CMakeRuntimeFlavor>CoreCLR</CMakeRuntimeFlavor>
+  </PropertyGroup>
+
+  <Import Project="native.targets" />
+
+  <ItemGroup>
+    <ProjectReference Include="..\java-runtime\java-runtime.csproj" ReferenceOutputAssembly="False" />
+  </ItemGroup>
+</Project>
diff --git a/src/native/native.targets b/src/native/native.targets
index 6b997259f4a..a7825885943 100644
--- a/src/native/native.targets
+++ b/src/native/native.targets
@@ -112,8 +112,8 @@
     <PropertyGroup>
       <_NoInline Condition=" '$(DoNotInlineMonodroid)' == 'true' ">-DDONT_INLINE=ON</_NoInline>
       <_NoStrip Condition=" '$(DoNotStripMonodroid)' == 'true' ">-DSTRIP_DEBUG=OFF</_NoStrip>
-
-      <_CmakeAndroidFlags>-DOUTPUT_PATH="$(OutputPath.TrimEnd('\'))" -DRUNTIME_FLAVOR="$(CMakeRuntimeFlavor)" $(_NoInline) $(_NoStrip) "$(MSBuildThisFileDirectory)"</_CmakeAndroidFlags>
+      <_LocalDotNetRuntimePath Condition=" '$(CLRLocalRuntimePath)' != '' And '$(CMakeRuntimeFlavor)' == 'CoreCLR' ">-DLOCAL_CORECLR_PATH="$(CLRLocalRuntimePath)"</_LocalDotNetRuntimePath>
+      <_CmakeAndroidFlags>-DOUTPUT_PATH="$(OutputPath.TrimEnd('\'))" -DRUNTIME_FLAVOR="$(CMakeRuntimeFlavor)" $(_NoInline) $(_NoStrip) $(_LocalDotNetRuntimePath) "$(MSBuildThisFileDirectory)"</_CmakeAndroidFlags>
     </PropertyGroup>
 
     <ItemGroup>