diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index d51bda348b4ad..f6338d7c04cc8 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3679,4 +3679,7 @@ The assembly update failed. - \ No newline at end of file + + Method body replacement not supported in this runtime. + + diff --git a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs index c254e1b67e2be..050c3266c0970 100644 --- a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs @@ -12,9 +12,7 @@ class NonRuntimeAssembly : Assembly } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/45689", platforms: TestPlatforms.AnyUnix, runtimes: TestRuntimes.Mono)] - [PlatformSpecific(TestPlatforms.Windows)] - [SkipOnMono("Not yet implemented on Mono")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/45689", platforms: ~TestPlatforms.Windows, runtimes: TestRuntimes.CoreCLR)] public static void ApplyUpdateInvalidParameters() { // Dummy delta arrays diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml index a80a66e4a682a..ce6945b1108ee 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml @@ -548,11 +548,6 @@ - - - - - @@ -637,5 +632,12 @@ + + + + + + + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs index 2f6e1e65e557c..337f1f516b1e6 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace System.Reflection.Metadata { public static class AssemblyExtensions @@ -8,6 +10,59 @@ public static class AssemblyExtensions [CLSCompliant(false)] public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) => throw new NotImplementedException(); - public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) => throw new NotImplementedException(); + /// + /// Updates the specified assembly using the provided metadata, IL and PDB deltas. + /// + /// + /// Currently executing methods will continue to use the existing IL. New executions of modified methods will + /// use the new IL. Different runtimes may have different limitations on what kinds of changes are supported, + /// and runtimes make no guarantees as to the state of the assembly and process if the delta includes + /// unsupported changes. + /// + /// The assembly to update. + /// The metadata changes to be applied. + /// The IL changes to be applied. + /// The PDB changes to be applied. + /// The assembly argument is null. + /// The update could not be applied. + public static void ApplyUpdate(Assembly assembly, ReadOnlySpan metadataDelta, ReadOnlySpan ilDelta, ReadOnlySpan pdbDelta) + { + if (assembly is not RuntimeAssembly runtimeAssembly) + { + if (assembly is null) throw new ArgumentNullException(nameof(assembly)); + throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly); + } + + // System.Private.CoreLib is not editable + if (runtimeAssembly == typeof(AssemblyExtensions).Assembly) + throw new InvalidOperationException (SR.InvalidOperation_AssemblyNotEditable); + +#if !FEATURE_METADATA_UPDATE + throw new NotSupportedException (SR.NotSupported_MethodBodyReplacement); +#else + unsafe + { + IntPtr monoAssembly = runtimeAssembly.GetUnderlyingNativeHandle (); + fixed (byte* metadataDeltaPtr = metadataDelta, ilDeltaPtr = ilDelta, pdbDeltaPtr = pdbDelta) + { + ApplyUpdate_internal(monoAssembly, metadataDeltaPtr, metadataDelta.Length, ilDeltaPtr, ilDelta.Length, pdbDeltaPtr, pdbDelta.Length); + } + } +#endif + } + + internal static void ApplyUpdateSdb(Assembly assembly, byte[] metadataDelta, byte[] ilDelta, byte[]? pdbDelta) + { + ReadOnlySpan md = metadataDelta; + ReadOnlySpan il = ilDelta; + ReadOnlySpan dpdb = pdbDelta == null ? default : pdbDelta; + ApplyUpdate (assembly, md, il, dpdb); + } + +#if FEATURE_METADATA_UPDATE + [MethodImpl (MethodImplOptions.InternalCall)] + private static unsafe extern void ApplyUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length, byte *dpdb_bytes, int dpdb_length); +#endif + } } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs index 58c0efb6c90d0..e54e4dda91c8e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs @@ -17,24 +17,5 @@ public static bool IsDynamicCodeCompiled [Intrinsic] // the JIT/AOT compiler will change this flag to false for FullAOT scenarios, otherwise true get => IsDynamicCodeCompiled; } - -#if !FEATURE_METADATA_UPDATE - internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { - throw new NotSupportedException ("Method body replacement not supported in this runtime"); - } -#else - [MethodImplAttribute (MethodImplOptions.InternalCall)] - private static unsafe extern void LoadMetadataUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length); - - internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { - unsafe { - fixed (byte* dmeta_bytes = dmeta_data) - fixed (byte* dil_bytes = dil_data) { - IntPtr mono_assembly = ((RuntimeAssembly)assm).GetUnderlyingNativeHandle (); - LoadMetadataUpdate_internal (mono_assembly, dmeta_bytes, dmeta_data.Length, dil_bytes, dil_data.Length); - } - } - } -#endif } } diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index f9ce2f0bb76f9..1d233f24600d1 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -239,7 +239,7 @@ ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int #endif #ifdef ENABLE_METADATA_UPDATE -ICALL_EXPORT void ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len); +ICALL_EXPORT void ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len, gconstpointer dpdb_bytes, int32_t dpdb_len); #endif #endif // __MONO_METADATA_ICALL_DECL_H__ diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index ac9d50b0dde13..73c3dfca687c4 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -239,6 +239,11 @@ HANDLES(FILEDI_1, "get_marshal_info", ves_icall_System_Reflection_FieldInfo_get_ HANDLES(FILEDI_2, "internal_from_handle_type", ves_icall_System_Reflection_FieldInfo_internal_from_handle_type, MonoReflectionField, 2, (MonoClassField_ref, MonoType_ref)) +#ifdef ENABLE_METADATA_UPDATE +ICALL_TYPE(ASSMEXT, "System.Reflection.Metadata.AssemblyExtensions", ASSMEXT_1) +NOHANDLES(ICALL(ASSMEXT_1, "ApplyUpdate_internal", ves_icall_AssemblyExtensions_ApplyUpdate)) +#endif + ICALL_TYPE(MBASE, "System.Reflection.MethodBase", MBASE_1) HANDLES(MBASE_1, "GetCurrentMethod", ves_icall_GetCurrentMethod, MonoReflectionMethod, 0, ()) @@ -324,11 +329,6 @@ HANDLES_REUSE_WRAPPER(MPROP_3, "get_metadata_token", ves_icall_reflection_get_to HANDLES(MPROP_4, "get_property_info", ves_icall_RuntimePropertyInfo_get_property_info, void, 3, (MonoReflectionProperty, MonoPropertyInfo_ref, PInfo)) HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_RuntimePropertyInfo_internal_from_handle_type, MonoReflectionProperty, 2, (MonoProperty_ptr, MonoType_ptr)) -#ifdef ENABLE_METADATA_UPDATE -ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1) -NOHANDLES(ICALL(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate)) -#endif - ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject)) HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 7dafe39c94f11..7f2d87314cfd9 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -5842,15 +5842,17 @@ ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportab #ifdef ENABLE_METADATA_UPDATE void -ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm, +ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, - gconstpointer dil_bytes, int32_t dil_len) + gconstpointer dil_bytes, int32_t dil_len, + gconstpointer dpdb_bytes, int32_t dpdb_len) { ERROR_DECL (error); g_assert (assm); g_assert (dmeta_len >= 0); MonoImage *image_base = assm->image; g_assert (image_base); + // TODO: use dpdb_bytes MonoDomain *domain = mono_domain_get (); mono_image_load_enc_delta (domain, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); diff --git a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs index 7188782df8b5e..89d025403bd4d 100644 --- a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs +++ b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -1,79 +1,104 @@ using System; using System.Reflection; +using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Collections.Generic; namespace MonoDelta { - public class DeltaHelper { - const string name = "System.Runtime.CompilerServices.RuntimeFeature"; + public class DeltaHelper { + private static Action _updateMethod; - private static MethodBase _updateMethod; + private static Action UpdateMethod => _updateMethod ?? InitUpdateMethod(); - private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod(); + private static Action InitUpdateMethod () + { + var monoType = typeof(System.Reflection.Metadata.AssemblyExtensions); + const string methodName = "ApplyUpdate"; + var mi = monoType.GetMethod (methodName, BindingFlags.Public | BindingFlags.Static); + if (mi == null) + throw new Exception ($"Couldn't get {methodName} from {monoType.FullName}"); + _updateMethod = MakeUpdateMethod (mi); //Delegate.CreateDelegate (typeof(Action), mi) as Action; + return _updateMethod; + } - private static MethodBase InitUpdateMethod () - { - var monoType = Type.GetType (name, throwOnError: true); - if (monoType == null) - throw new Exception ($"Couldn't get the type {name}"); - _updateMethod = monoType.GetMethod ("LoadMetadataUpdate", BindingFlags.NonPublic | BindingFlags.Static); - if (_updateMethod == null) - throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}"); - return _updateMethod; - } + private static Action MakeUpdateMethod (MethodInfo applyUpdate) + { + // Make + // void ApplyUpdateArray (Assembly a, byte[] dmeta, byte[] dil, byte[] dpdb) + // { + // ApplyUpdate (a, (ReadOnlySpan)dmeta, (ReadOnlySpan)dil, (ReadOnlySpan)dpdb); + // } + var dm = new DynamicMethod ("CallApplyUpdate", typeof(void), new Type[] { typeof(Assembly), typeof(byte[]), typeof(byte[]), typeof(byte[])}, typeof (DeltaHelper).Module); + var ilg = dm.GetILGenerator (); + var conv = typeof(ReadOnlySpan).GetMethod("op_Implicit", new Type[] {typeof(byte[])}); - private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) - { - UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data}); - } + ilg.Emit (OpCodes.Ldarg_0); + ilg.Emit (OpCodes.Ldarg_1); + ilg.Emit (OpCodes.Call, conv); + ilg.Emit (OpCodes.Ldarg_2); + ilg.Emit (OpCodes.Call, conv); + ilg.Emit (OpCodes.Ldarg_3); + ilg.Emit (OpCodes.Call, conv); + ilg.Emit (OpCodes.Call, applyUpdate); + ilg.Emit (OpCodes.Ret); - DeltaHelper () { } + return dm.CreateDelegate(typeof(Action)) as Action; + } - public static DeltaHelper Make () - { - return new DeltaHelper (); - } + private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data, byte[] dpdb_data) + { + UpdateMethod (assm, dmeta_data, dil_data, dpdb_data); + } - public static void InjectUpdate (string assemblyName, string dmeta_base64, string dil_base64) { - var an = new AssemblyName (assemblyName); - Assembly assm = null; - /* TODO: non-default ALCs */ - foreach (var candidate in AssemblyLoadContext.Default.Assemblies) { - if (candidate.GetName().Name == an.Name) { - assm = candidate; - break; - } - } - if (assm == null) - throw new ArgumentException ("assemblyName"); - var dmeta_data = Convert.FromBase64String (dmeta_base64); - var dil_data = Convert.FromBase64String (dil_base64); - LoadMetadataUpdate (assm, dmeta_data, dil_data); - } + DeltaHelper () { } - private Dictionary assembly_count = new Dictionary (); + public static DeltaHelper Make () + { + return new DeltaHelper (); + } - public void Update (Assembly assm) { - int count; - if (!assembly_count.TryGetValue (assm, out count)) - count = 1; - else - count++; - assembly_count [assm] = count; + public static void InjectUpdate (string assemblyName, string dmeta_base64, string dil_base64) { + var an = new AssemblyName (assemblyName); + Assembly assm = null; + /* TODO: non-default ALCs */ + foreach (var candidate in AssemblyLoadContext.Default.Assemblies) { + if (candidate.GetName().Name == an.Name) { + assm = candidate; + break; + } + } + if (assm == null) + throw new ArgumentException ("assemblyName"); + var dmeta_data = Convert.FromBase64String (dmeta_base64); + var dil_data = Convert.FromBase64String (dil_base64); + byte[] dpdb_data = null; + LoadMetadataUpdate (assm, dmeta_data, dil_data, dpdb_data); + } - /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */ - string basename = assm.Location; - if (basename == "") - basename = assm.GetName().Name + ".dll"; - Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}"); + private Dictionary assembly_count = new Dictionary (); - string dmeta_name = $"{basename}.{count}.dmeta"; - string dil_name = $"{basename}.{count}.dil"; - byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name); - byte[] dil_data = System.IO.File.ReadAllBytes (dil_name); + public void Update (Assembly assm) { + int count; + if (!assembly_count.TryGetValue (assm, out count)) + count = 1; + else + count++; + assembly_count [assm] = count; - LoadMetadataUpdate (assm, dmeta_data, dil_data); - } - } + /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */ + string basename = assm.Location; + if (basename == "") + basename = assm.GetName().Name + ".dll"; + Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}"); + + string dmeta_name = $"{basename}.{count}.dmeta"; + string dil_name = $"{basename}.{count}.dil"; + byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name); + byte[] dil_data = System.IO.File.ReadAllBytes (dil_name); + byte[] dpdb_data = null; // TODO also use the dpdb data + + LoadMetadataUpdate (assm, dmeta_data, dil_data, dpdb_data); + } + } }